replicas-cli 0.2.182 → 0.2.184
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +671 -152
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -7316,7 +7316,7 @@ var require_dist = __commonJS({
|
|
|
7316
7316
|
// src/index.ts
|
|
7317
7317
|
import "dotenv/config";
|
|
7318
7318
|
import { Command, InvalidArgumentError } from "commander";
|
|
7319
|
-
import
|
|
7319
|
+
import chalk22 from "chalk";
|
|
7320
7320
|
|
|
7321
7321
|
// src/commands/login.ts
|
|
7322
7322
|
import http from "http";
|
|
@@ -7603,7 +7603,7 @@ function generateState() {
|
|
|
7603
7603
|
}
|
|
7604
7604
|
async function loginCommand() {
|
|
7605
7605
|
const state = generateState();
|
|
7606
|
-
return new Promise((
|
|
7606
|
+
return new Promise((resolve2, reject) => {
|
|
7607
7607
|
let authTimeout;
|
|
7608
7608
|
let hasHandledCallback = false;
|
|
7609
7609
|
let lastRedirectUrl = null;
|
|
@@ -7740,7 +7740,7 @@ async function loginCommand() {
|
|
|
7740
7740
|
setImmediate(() => {
|
|
7741
7741
|
server.closeAllConnections?.();
|
|
7742
7742
|
server.close();
|
|
7743
|
-
|
|
7743
|
+
resolve2();
|
|
7744
7744
|
});
|
|
7745
7745
|
} catch (error2) {
|
|
7746
7746
|
const errorUrl = `${WEB_APP_URL}/cli-login/error?message=${encodeURIComponent("Failed to verify authentication.")}`;
|
|
@@ -7843,13 +7843,13 @@ import chalk5 from "chalk";
|
|
|
7843
7843
|
import { spawn } from "child_process";
|
|
7844
7844
|
var SSH_OPTIONS = ["-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null"];
|
|
7845
7845
|
async function connectSSH(token, host, proxyCommand) {
|
|
7846
|
-
return new Promise((
|
|
7846
|
+
return new Promise((resolve2, reject) => {
|
|
7847
7847
|
const sshArgs = proxyCommand ? [...SSH_OPTIONS, "-o", `ProxyCommand=${proxyCommand}`, `${token}@${host}`] : [...SSH_OPTIONS, `${token}@${host}`];
|
|
7848
7848
|
const ssh = spawn("ssh", sshArgs, {
|
|
7849
7849
|
stdio: "inherit"
|
|
7850
7850
|
});
|
|
7851
7851
|
ssh.on("close", () => {
|
|
7852
|
-
|
|
7852
|
+
resolve2();
|
|
7853
7853
|
});
|
|
7854
7854
|
ssh.on("error", reject);
|
|
7855
7855
|
});
|
|
@@ -8105,8 +8105,200 @@ function extractPrNumber(url) {
|
|
|
8105
8105
|
return parsed ? String(parsed.number) : null;
|
|
8106
8106
|
}
|
|
8107
8107
|
|
|
8108
|
+
// ../shared/src/default-skills/replicas-agent/abilities/computer.ts
|
|
8109
|
+
var SECTION = `### Computer use (Linux desktop control)
|
|
8110
|
+
Drive the workspace's Linux desktop - open a browser, click, type, scroll, screenshot, record - and surface a live noVNC viewer to the user via the \`replicas computer\` CLI. Every Replicas workspace boots with Xvfb / openbox / x11vnc / noVNC pre-installed.
|
|
8111
|
+
|
|
8112
|
+
**Reference:** \`references/COMPUTER-USE.md\`
|
|
8113
|
+
|
|
8114
|
+
Use this when:
|
|
8115
|
+
- A task requires interacting with a website, web app, or desktop application that has no usable API
|
|
8116
|
+
- You want the user to watch the agent work - \`replicas computer start\` exposes a \`Desktop\` tab in the dashboard
|
|
8117
|
+
- You're testing UI changes in a browser before reporting them as done
|
|
8118
|
+
- You want to record a screen capture of a task as proof to share back`;
|
|
8119
|
+
var REFERENCE = `# Computer use (Linux desktop control)
|
|
8120
|
+
|
|
8121
|
+
Every Replicas workspace boots with a full Linux desktop stack - Xvfb (1920\xD71080), openbox, tint2, x11vnc, noVNC, ffmpeg, and Google Chrome. You drive it through the \`replicas computer\` CLI.
|
|
8122
|
+
|
|
8123
|
+
Use this for anything the user can't reasonably do via an API - clicking around web apps, filling forms, testing UI changes, dragging files between desktop apps, recording a walkthrough.
|
|
8124
|
+
|
|
8125
|
+
## When to use it
|
|
8126
|
+
|
|
8127
|
+
- **Prefer real APIs first.** If a task has a CLI or HTTP API (GitHub, Linear, Slack, Replicas itself), use that. Driving a UI is slower, flakier, and less auditable.
|
|
8128
|
+
- **Use it when there's no API**: testing a frontend you just changed, navigating a vendor portal, demonstrating a flow on video.
|
|
8129
|
+
- **Use it when the user wants to watch.** \`replicas computer start\` exposes a live \`Desktop\` tab in the dashboard - they can watch you work in real time.
|
|
8130
|
+
|
|
8131
|
+
## Never use raw \`xdotool\` / \`scrot\` / \`ffmpeg\` directly
|
|
8132
|
+
|
|
8133
|
+
The CLI is the canonical surface. Chat transcripts that show \`DISPLAY=:99 xdotool key Return\` are noisy and break when the underlying stack changes. Always call the CLI:
|
|
8134
|
+
|
|
8135
|
+
\`\`\`bash
|
|
8136
|
+
# DO
|
|
8137
|
+
replicas computer key Return
|
|
8138
|
+
replicas computer screenshot /tmp/state.png
|
|
8139
|
+
|
|
8140
|
+
# DON'T
|
|
8141
|
+
DISPLAY=:99 xdotool key Return
|
|
8142
|
+
scrot /tmp/state.png
|
|
8143
|
+
\`\`\`
|
|
8144
|
+
|
|
8145
|
+
## Quickstart
|
|
8146
|
+
|
|
8147
|
+
\`\`\`bash
|
|
8148
|
+
# 1) Make the desktop visible to the user (creates an authenticated noVNC
|
|
8149
|
+
# preview on port 6080, prints the viewer URL, adds a "Desktop" tab to the
|
|
8150
|
+
# dashboard).
|
|
8151
|
+
replicas computer start
|
|
8152
|
+
|
|
8153
|
+
# 2) Launch a browser on the workspace display.
|
|
8154
|
+
replicas computer launch chrome
|
|
8155
|
+
|
|
8156
|
+
# 3) Take a screenshot so you can see what's there.
|
|
8157
|
+
replicas computer screenshot /tmp/state.png
|
|
8158
|
+
# (Read the PNG yourself before deciding where to click.)
|
|
8159
|
+
|
|
8160
|
+
# 4) Drive the UI.
|
|
8161
|
+
replicas computer key ctrl+l # focus address bar
|
|
8162
|
+
replicas computer type "https://news.ycombinator.com"
|
|
8163
|
+
replicas computer key Return
|
|
8164
|
+
replicas computer click 521 700 # click coordinates from the screenshot
|
|
8165
|
+
replicas computer scroll down --amount 5
|
|
8166
|
+
|
|
8167
|
+
# 5) (Optional) Record a screencap to share back.
|
|
8168
|
+
replicas computer record start /tmp/demo.mp4 --fps 60
|
|
8169
|
+
# ... do stuff ...
|
|
8170
|
+
replicas computer record stop
|
|
8171
|
+
replicas media upload /tmp/demo.mp4
|
|
8172
|
+
|
|
8173
|
+
# 6) Tear down the live preview when done (services keep running for next time).
|
|
8174
|
+
replicas computer stop
|
|
8175
|
+
\`\`\`
|
|
8176
|
+
|
|
8177
|
+
## Command reference
|
|
8178
|
+
|
|
8179
|
+
### \`replicas computer start [--port N] [--display :N] [--size WxH]\`
|
|
8180
|
+
Ensures all desktop services are running and creates an authenticated noVNC preview. Prints the viewer URL (\`https://<port>-<hash>.tryreplicas.com/\`). The Replicas dashboard automatically shows a \`Desktop\` tab while this preview is live - point the user at it instead of pasting the URL.
|
|
8181
|
+
|
|
8182
|
+
Idempotent - safe to call repeatedly. Use it as the first computer-use command in any session.
|
|
8183
|
+
|
|
8184
|
+
### \`replicas computer stop [--port N]\`
|
|
8185
|
+
Tears down the noVNC preview (the \`Desktop\` tab disappears). The underlying Xvfb / openbox / x11vnc / browser keep running so the next \`start\` is instant.
|
|
8186
|
+
|
|
8187
|
+
### \`replicas computer status\`
|
|
8188
|
+
Prints which desktop services are running and the active preview URL (if any). Useful for debugging when a tool call seems to be doing nothing.
|
|
8189
|
+
|
|
8190
|
+
### \`replicas computer screenshot <path>\`
|
|
8191
|
+
Captures the current desktop to a PNG at the given path. Read the file (e.g. with your Read tool) to see what's on screen - coordinates from the screenshot drive subsequent \`click\` / \`move\` / \`drag\` calls.
|
|
8192
|
+
|
|
8193
|
+
### \`replicas computer click <x> <y> [--button N] [--double] [--modifiers ctrl+shift]\`
|
|
8194
|
+
Move to (x, y) and click. Default is left-click (button 1); pass \`--button 3\` for right-click. \`--modifiers\` holds keys during the click (e.g. ctrl-click a link to open in a new tab).
|
|
8195
|
+
|
|
8196
|
+
### \`replicas computer move <x> <y>\`
|
|
8197
|
+
Move the mouse without clicking. Useful for hovering tooltips.
|
|
8198
|
+
|
|
8199
|
+
### \`replicas computer type <text> [--delay MS]\`
|
|
8200
|
+
Type a literal string into the focused field. Default per-character delay is 12ms (~80 wpm) - feels human and avoids breaking apps that debounce input. Bump \`--delay 30\` for stricter apps.
|
|
8201
|
+
|
|
8202
|
+
For key combos (not literal text), use \`key\`. \`type "ctrl+l"\` will literally type the seven characters \`c t r l + l\`.
|
|
8203
|
+
|
|
8204
|
+
### \`replicas computer key <combo>\`
|
|
8205
|
+
Press a single key or combo. Examples: \`Return\`, \`Escape\`, \`Tab\`, \`ctrl+l\`, \`ctrl+shift+t\`, \`alt+Left\`, \`Page_Down\`, \`Home\`. Syntax matches \`xdotool key\`.
|
|
8206
|
+
|
|
8207
|
+
### \`replicas computer scroll <up|down|left|right> [--amount N] [--x X --y Y]\`
|
|
8208
|
+
Scroll the wheel. Pass \`--x\` / \`--y\` to hover before scrolling (otherwise scrolls wherever the cursor currently is). Default amount is 3 wheel ticks.
|
|
8209
|
+
|
|
8210
|
+
### \`replicas computer drag <fromX> <fromY> <toX> <toY>\`
|
|
8211
|
+
Press left mouse at (fromX, fromY), drag to (toX, toY), release. For things like dragging a file onto an upload zone.
|
|
8212
|
+
|
|
8213
|
+
### \`replicas computer launch <app> [args...]\`
|
|
8214
|
+
Spawns an app on the workspace display. Built-in aliases:
|
|
8215
|
+
- \`chrome\` - Google Chrome with a clean profile and sane flags
|
|
8216
|
+
- \`chromium\` - Chromium variant
|
|
8217
|
+
- \`firefox\` - Firefox
|
|
8218
|
+
- \`terminal\` - xfce4-terminal (modern, themed); \`xterm\` for the classic
|
|
8219
|
+
- \`notepad\` / \`editor\` - mousepad (lightweight GTK text editor)
|
|
8220
|
+
- \`files\` / \`filemanager\` - thunar (file manager)
|
|
8221
|
+
Anything else gets \`exec\`'d verbatim, so \`replicas computer launch xeyes\` works if xeyes is installed.
|
|
8222
|
+
|
|
8223
|
+
### \`replicas computer record start <path> [--fps N]\`
|
|
8224
|
+
Starts an ffmpeg screen recorder. Output is a fragmented MP4 (still playable if the workspace dies mid-record). Default 60fps; drop to 30 if the workspace is CPU-constrained.
|
|
8225
|
+
|
|
8226
|
+
Only one recording at a time. Re-running \`start\` while one is active fails - call \`stop\` first.
|
|
8227
|
+
|
|
8228
|
+
### \`replicas computer record stop\`
|
|
8229
|
+
SIGINTs ffmpeg, waits for it to finalize the MP4, prints the output path. Upload it with \`replicas media upload <path>\` to share it.
|
|
8230
|
+
|
|
8231
|
+
## Patterns
|
|
8232
|
+
|
|
8233
|
+
### Action / screenshot loop
|
|
8234
|
+
You are blind between tool calls. After any action that changes the screen, take a screenshot before deciding the next coordinate:
|
|
8235
|
+
|
|
8236
|
+
\`\`\`bash
|
|
8237
|
+
replicas computer click 521 700
|
|
8238
|
+
sleep 2 # let the page settle
|
|
8239
|
+
replicas computer screenshot /tmp/after-click.png
|
|
8240
|
+
# read /tmp/after-click.png, decide next click
|
|
8241
|
+
\`\`\`
|
|
8242
|
+
|
|
8243
|
+
\`sleep\` is a regular shell sleep - there's no \`replicas computer wait\` command, but you can mix shell sleeps freely.
|
|
8244
|
+
|
|
8245
|
+
### Typing into an address bar
|
|
8246
|
+
\`\`\`bash
|
|
8247
|
+
replicas computer key ctrl+l # focus address bar
|
|
8248
|
+
replicas computer type "https://example.com"
|
|
8249
|
+
replicas computer key Return
|
|
8250
|
+
sleep 3 # wait for page load
|
|
8251
|
+
replicas computer screenshot /tmp/loaded.png
|
|
8252
|
+
\`\`\`
|
|
8253
|
+
|
|
8254
|
+
### Coordinates from screenshots
|
|
8255
|
+
The display is 1920\xD71080. Screenshot pixels map 1:1 to click coordinates - if your Read tool shows a button at pixel (520, 700), click \`replicas computer click 520 700\`. **No translation needed.** Modern image-reading models often imagine the screenshot is at a different resolution; trust the \`xdpyinfo\` value (\`replicas computer status\` shows the real size).
|
|
8256
|
+
|
|
8257
|
+
### Letting the user watch
|
|
8258
|
+
Always start the desktop session with \`replicas computer start\` *before* doing anything visual, even if you don't need the URL yourself. The Desktop tab appears in their dashboard. They get to watch and intervene if needed.
|
|
8259
|
+
|
|
8260
|
+
### Recording a deliverable
|
|
8261
|
+
For tasks the user wants proof of:
|
|
8262
|
+
\`\`\`bash
|
|
8263
|
+
replicas computer start # makes it visible live too
|
|
8264
|
+
replicas computer record start /tmp/walkthrough.mp4
|
|
8265
|
+
# ... your work ...
|
|
8266
|
+
replicas computer record stop
|
|
8267
|
+
replicas media upload /tmp/walkthrough.mp4
|
|
8268
|
+
\`\`\`
|
|
8269
|
+
Then embed the printed \`\` line in your chat reply. See \`MEDIA.md\`.
|
|
8270
|
+
|
|
8271
|
+
### Cleaning up
|
|
8272
|
+
Call \`replicas computer stop\` when you're done with the visual demo so the live preview URL goes away. The services keep running so the next \`start\` is instant.
|
|
8273
|
+
|
|
8274
|
+
## Failure modes
|
|
8275
|
+
|
|
8276
|
+
- **"Desktop services script missing"**: workspace image is older than this skill. Tell the user - nothing you can do from the CLI side.
|
|
8277
|
+
- **\`xdotool ... failed: Can't open display\`**: Xvfb didn't come up. \`replicas computer status\` will show which service is dead. Re-running any CLI command auto-attempts to start it.
|
|
8278
|
+
- **Browser doesn't appear after \`launch chrome\`**: give it 1-2s, then screenshot. Chrome cold-start on the virtual display takes ~500ms but bigger pages take longer.
|
|
8279
|
+
- **Live preview shows static / black screen**: the browser may have crashed. \`replicas computer status\` should show no Chrome process - re-launch.
|
|
8280
|
+
|
|
8281
|
+
## What gets baked in vs. lazy
|
|
8282
|
+
|
|
8283
|
+
| Component | Where |
|
|
8284
|
+
|---|---|
|
|
8285
|
+
| \`xvfb\`, \`openbox\`, \`tint2\`, \`x11vnc\`, \`websockify\`, \`xdotool\`, \`scrot\`, \`ffmpeg\`, \`google-chrome\` | Baked into the workspace image |
|
|
8286
|
+
| Xvfb / openbox / tint2 / x11vnc / websockify processes | Started at workspace boot (\`replicas-start-desktop-services\`) |
|
|
8287
|
+
| noVNC preview URL (port 6080) | **Lazy** - created by \`replicas computer start\` |
|
|
8288
|
+
| Dashboard \`Desktop\` tab | Appears when the preview URL is live, disappears on \`stop\` |
|
|
8289
|
+
|
|
8290
|
+
You can call any of the input tools (\`click\`, \`type\`, \`screenshot\`, etc.) without first calling \`start\` - they'll work since the daemons are running. \`start\` is only needed when you want the user to see the live stream.
|
|
8291
|
+
`;
|
|
8292
|
+
var COMPUTER_ABILITY = {
|
|
8293
|
+
label: "Computer use",
|
|
8294
|
+
description: "Drive the workspace's Linux desktop and stream a live noVNC viewer.",
|
|
8295
|
+
bullet: "- Driving the workspace Linux desktop (open a browser, click, type, scroll, screenshot, record) and exposing a live noVNC viewer in the dashboard",
|
|
8296
|
+
section: SECTION,
|
|
8297
|
+
referenceFile: { name: "COMPUTER-USE.md", content: REFERENCE }
|
|
8298
|
+
};
|
|
8299
|
+
|
|
8108
8300
|
// ../shared/src/default-skills/replicas-agent/abilities/docker.ts
|
|
8109
|
-
var
|
|
8301
|
+
var SECTION2 = `### Docker
|
|
8110
8302
|
Start and use the Docker daemon in Replicas workspaces. Docker is pre-installed but the daemon does not auto-start.
|
|
8111
8303
|
|
|
8112
8304
|
**Reference:** \`references/DOCKER.md\`
|
|
@@ -8115,7 +8307,7 @@ Use this when:
|
|
|
8115
8307
|
- You need to run \`docker\` or \`docker compose\` commands
|
|
8116
8308
|
- You need to build or run Docker containers
|
|
8117
8309
|
- Your task involves containerized services or Docker-based workflows`;
|
|
8118
|
-
var
|
|
8310
|
+
var REFERENCE2 = `# Docker
|
|
8119
8311
|
|
|
8120
8312
|
Docker is pre-installed in Replicas workspaces, but the daemon does **not** auto-start. You must start it manually before running any \`docker\` or \`docker compose\` commands.
|
|
8121
8313
|
|
|
@@ -8144,12 +8336,12 @@ var DOCKER_ABILITY = {
|
|
|
8144
8336
|
label: "Docker",
|
|
8145
8337
|
description: "Start the daemon, build/run containers, drive `docker compose`.",
|
|
8146
8338
|
bullet: "- Using Docker (starting the daemon, running containers, docker compose)",
|
|
8147
|
-
section:
|
|
8148
|
-
referenceFile: { name: "DOCKER.md", content:
|
|
8339
|
+
section: SECTION2,
|
|
8340
|
+
referenceFile: { name: "DOCKER.md", content: REFERENCE2 }
|
|
8149
8341
|
};
|
|
8150
8342
|
|
|
8151
8343
|
// ../shared/src/default-skills/replicas-agent/abilities/github.ts
|
|
8152
|
-
var
|
|
8344
|
+
var SECTION3 = `### GitHub
|
|
8153
8345
|
Use the pre-authenticated \`gh\` CLI for pull requests, issues, actions, and API calls.
|
|
8154
8346
|
|
|
8155
8347
|
**Reference:** \`references/GITHUB.md\`
|
|
@@ -8159,7 +8351,7 @@ Use this when:
|
|
|
8159
8351
|
- You need to interact with GitHub issues or actions
|
|
8160
8352
|
- You need to use the GitHub API for advanced operations
|
|
8161
8353
|
- You need to include images in PR descriptions`;
|
|
8162
|
-
var
|
|
8354
|
+
var REFERENCE3 = `# GitHub Integration
|
|
8163
8355
|
|
|
8164
8356
|
This guide covers how to interact with GitHub from within your Replicas workspace.
|
|
8165
8357
|
|
|
@@ -8285,12 +8477,12 @@ var GITHUB_ABILITY = {
|
|
|
8285
8477
|
label: "GitHub",
|
|
8286
8478
|
description: "Pre-authenticated `gh` CLI for PRs, issues, releases, and GraphQL.",
|
|
8287
8479
|
bullet: "- Interacting with GitHub (creating PRs, managing issues, using the API, etc.)",
|
|
8288
|
-
section:
|
|
8289
|
-
referenceFile: { name: "GITHUB.md", content:
|
|
8480
|
+
section: SECTION3,
|
|
8481
|
+
referenceFile: { name: "GITHUB.md", content: REFERENCE3 }
|
|
8290
8482
|
};
|
|
8291
8483
|
|
|
8292
8484
|
// ../shared/src/default-skills/replicas-agent/abilities/google.ts
|
|
8293
|
-
var
|
|
8485
|
+
var SECTION4 = `### Google Workspace (Docs, Sheets, Forms, Drive)
|
|
8294
8486
|
Create and edit Google Docs, Sheets, and Forms via the Replicas gateway. Files are owned by Replicas \u2014 the integration cannot access pre-existing Google content created outside of it.
|
|
8295
8487
|
|
|
8296
8488
|
**Reference:** \`references/GOOGLE.md\`
|
|
@@ -8299,7 +8491,7 @@ Use this when:
|
|
|
8299
8491
|
- You need to create or edit a Google Doc, Sheet, or Form
|
|
8300
8492
|
- You need to share, rename, move, or delete a Replicas-created Google file
|
|
8301
8493
|
- You need to read responses from a Replicas-created Google Form`;
|
|
8302
|
-
var
|
|
8494
|
+
var REFERENCE4 = `# Google Workspace (Docs, Sheets, Forms, Drive)
|
|
8303
8495
|
|
|
8304
8496
|
This guide covers how to create and edit Google Docs, Sheets, and Forms \u2014 plus do basic Drive file operations \u2014 from inside a Replicas workspace, using the monolith as a gateway to Google's APIs.
|
|
8305
8497
|
|
|
@@ -8560,12 +8752,12 @@ var GOOGLE_ABILITY = {
|
|
|
8560
8752
|
label: "Google Workspace",
|
|
8561
8753
|
description: "Create / edit Docs, Sheets, Forms, and Drive files via the Replicas gateway.",
|
|
8562
8754
|
bullet: "- Interacting with Google Workspace (creating and editing Docs, Sheets, and Forms, sharing files, reading form responses, etc.)",
|
|
8563
|
-
section:
|
|
8564
|
-
referenceFile: { name: "GOOGLE.md", content:
|
|
8755
|
+
section: SECTION4,
|
|
8756
|
+
referenceFile: { name: "GOOGLE.md", content: REFERENCE4 }
|
|
8565
8757
|
};
|
|
8566
8758
|
|
|
8567
8759
|
// ../shared/src/default-skills/replicas-agent/abilities/linear.ts
|
|
8568
|
-
var
|
|
8760
|
+
var SECTION5 = `### Linear
|
|
8569
8761
|
Fetch issues, update state, add comments, and search via the Linear GraphQL API.
|
|
8570
8762
|
|
|
8571
8763
|
**Reference:** \`references/LINEAR.md\`
|
|
@@ -8574,7 +8766,7 @@ Use this when:
|
|
|
8574
8766
|
- You encounter a Linear issue link and need to understand the task
|
|
8575
8767
|
- You need to update an issue's state (e.g. mark as done)
|
|
8576
8768
|
- You need to comment on or search for Linear issues`;
|
|
8577
|
-
var
|
|
8769
|
+
var REFERENCE5 = `# Linear Integration
|
|
8578
8770
|
|
|
8579
8771
|
This guide covers how to interact with Linear from within your Replicas workspace.
|
|
8580
8772
|
|
|
@@ -8649,12 +8841,12 @@ var LINEAR_ABILITY = {
|
|
|
8649
8841
|
label: "Linear",
|
|
8650
8842
|
description: "Fetch issues, post comments, update states via the Linear GraphQL API.",
|
|
8651
8843
|
bullet: "- Interacting with Linear (fetching issues, updating state, commenting, etc.)",
|
|
8652
|
-
section:
|
|
8653
|
-
referenceFile: { name: "LINEAR.md", content:
|
|
8844
|
+
section: SECTION5,
|
|
8845
|
+
referenceFile: { name: "LINEAR.md", content: REFERENCE5 }
|
|
8654
8846
|
};
|
|
8655
8847
|
|
|
8656
8848
|
// ../shared/src/default-skills/replicas-agent/abilities/media.ts
|
|
8657
|
-
var
|
|
8849
|
+
var SECTION6 = `### Media
|
|
8658
8850
|
Share screenshots, screen recordings, generated diagrams, and audio clips inline in the Replicas chat (and as references in external messages).
|
|
8659
8851
|
|
|
8660
8852
|
**Reference:** \`references/MEDIA.md\`
|
|
@@ -8663,7 +8855,7 @@ Use this when:
|
|
|
8663
8855
|
- You produce a screenshot, recording, generated image, or audio clip the user should see
|
|
8664
8856
|
- You record video output (browser automation, screen capture) \u2014 including the recommended aspect ratio and FPS
|
|
8665
8857
|
- You need to embed media in a Slack/Linear/GitHub message AND keep a referenceable copy in the Replicas dashboard`;
|
|
8666
|
-
var
|
|
8858
|
+
var REFERENCE6 = `# Media (Screenshots, Recordings, Audio)
|
|
8667
8859
|
|
|
8668
8860
|
This guide covers how to share screenshots, screen recordings, generated diagrams, and audio clips with the user inline in the Replicas chat.
|
|
8669
8861
|
|
|
@@ -8777,12 +8969,12 @@ var MEDIA_ABILITY = {
|
|
|
8777
8969
|
label: "Media",
|
|
8778
8970
|
description: "Share screenshots, recordings, generated images, and audio clips.",
|
|
8779
8971
|
bullet: "- Sharing media (screenshots, screen recordings, generated diagrams, audio clips) \u2014 including in your Replicas chat reply and to external platforms",
|
|
8780
|
-
section:
|
|
8781
|
-
referenceFile: { name: "MEDIA.md", content:
|
|
8972
|
+
section: SECTION6,
|
|
8973
|
+
referenceFile: { name: "MEDIA.md", content: REFERENCE6 }
|
|
8782
8974
|
};
|
|
8783
8975
|
|
|
8784
8976
|
// ../shared/src/default-skills/replicas-agent/abilities/previews.ts
|
|
8785
|
-
var
|
|
8977
|
+
var SECTION7 = `### Previews
|
|
8786
8978
|
Expose locally running services (web apps, APIs, databases) as public preview URLs so humans can interact with them directly.
|
|
8787
8979
|
|
|
8788
8980
|
**Reference:** \`references/PREVIEWS.md\`
|
|
@@ -8791,7 +8983,7 @@ Use this when:
|
|
|
8791
8983
|
- You need to start a service that a human should view or interact with
|
|
8792
8984
|
- The task involves UI work that benefits from human review
|
|
8793
8985
|
- You are verifying frontend/backend integrations visually`;
|
|
8794
|
-
var
|
|
8986
|
+
var REFERENCE7 = `# Preview URLs
|
|
8795
8987
|
|
|
8796
8988
|
When you run services on ports \u2014 such as a web app, API server, or database \u2014 humans may want to interact with them directly. You can expose your locally running services as public preview URLs.
|
|
8797
8989
|
|
|
@@ -8873,12 +9065,12 @@ var PREVIEWS_ABILITY = {
|
|
|
8873
9065
|
label: "Previews",
|
|
8874
9066
|
description: "Expose locally running services on public preview URLs for humans.",
|
|
8875
9067
|
bullet: "- Creating preview URLs for locally running services",
|
|
8876
|
-
section:
|
|
8877
|
-
referenceFile: { name: "PREVIEWS.md", content:
|
|
9068
|
+
section: SECTION7,
|
|
9069
|
+
referenceFile: { name: "PREVIEWS.md", content: REFERENCE7 }
|
|
8878
9070
|
};
|
|
8879
9071
|
|
|
8880
9072
|
// ../shared/src/default-skills/replicas-agent/abilities/replicas.ts
|
|
8881
|
-
var
|
|
9073
|
+
var SECTION8 = `### Replicas (in-workspace CLI)
|
|
8882
9074
|
Take action *with* Replicas itself \u2014 manage automations, environments (variables, files), repos, and \`replicas.json\` config \u2014 using the pre-installed, pre-authenticated \`replicas\` CLI.
|
|
8883
9075
|
|
|
8884
9076
|
**Reference:** \`references/REPLICAS.md\`
|
|
@@ -8888,7 +9080,7 @@ Use this when:
|
|
|
8888
9080
|
- The user asks you to manage environments, environment variables, or environment files
|
|
8889
9081
|
- The user asks "what envs / repos / automations do I have?"
|
|
8890
9082
|
- The user asks you to scaffold a \`replicas.json\` / \`replicas.yaml\` in a repo`;
|
|
8891
|
-
var
|
|
9083
|
+
var REFERENCE8 = `# Replicas (in-workspace CLI)
|
|
8892
9084
|
|
|
8893
9085
|
This guide covers how to take action *with* Replicas itself from inside a Replicas workspace \u2014 managing automations, environments (and their variables/files), repos, previews, and the user's \`replicas.json\` config \u2014 using the pre-installed \`replicas\` CLI.
|
|
8894
9086
|
|
|
@@ -9082,13 +9274,13 @@ var REPLICAS_ABILITY = {
|
|
|
9082
9274
|
description: "Teach the agent about Replicas itself \u2014 automations, environments, the in-workspace CLI.",
|
|
9083
9275
|
// No bullet — help_instructions covers the `replicas` CLI surface in detail.
|
|
9084
9276
|
bullet: "",
|
|
9085
|
-
section:
|
|
9086
|
-
referenceFile: { name: "REPLICAS.md", content:
|
|
9277
|
+
section: SECTION8,
|
|
9278
|
+
referenceFile: { name: "REPLICAS.md", content: REFERENCE8 },
|
|
9087
9279
|
locked: true
|
|
9088
9280
|
};
|
|
9089
9281
|
|
|
9090
9282
|
// ../shared/src/default-skills/replicas-agent/abilities/slack.ts
|
|
9091
|
-
var
|
|
9283
|
+
var SECTION9 = `### Slack
|
|
9092
9284
|
Send messages, read threads, search conversations, and upload files via the Slack Web API.
|
|
9093
9285
|
|
|
9094
9286
|
**Reference:** \`references/SLACK.md\`
|
|
@@ -9098,7 +9290,7 @@ Use this when:
|
|
|
9098
9290
|
- You need to read or fetch a Slack conversation
|
|
9099
9291
|
- You encounter a Slack message link and need to retrieve its content
|
|
9100
9292
|
- The task asks you to notify, update, or communicate via Slack`;
|
|
9101
|
-
var
|
|
9293
|
+
var REFERENCE9 = `# Slack Integration
|
|
9102
9294
|
|
|
9103
9295
|
This guide covers how to interact with Slack from within your Replicas workspace.
|
|
9104
9296
|
|
|
@@ -9171,13 +9363,14 @@ var SLACK_ABILITY = {
|
|
|
9171
9363
|
label: "Slack",
|
|
9172
9364
|
description: "Send messages, read threads, search conversations, upload files.",
|
|
9173
9365
|
bullet: "- Interacting with Slack (sending messages, reading threads, etc.)",
|
|
9174
|
-
section:
|
|
9175
|
-
referenceFile: { name: "SLACK.md", content:
|
|
9366
|
+
section: SECTION9,
|
|
9367
|
+
referenceFile: { name: "SLACK.md", content: REFERENCE9 }
|
|
9176
9368
|
};
|
|
9177
9369
|
|
|
9178
9370
|
// ../shared/src/default-skills/replicas-agent/registry.ts
|
|
9179
9371
|
var REPLICAS_AGENT_ABILITY_REGISTRY = {
|
|
9180
9372
|
replicas: REPLICAS_ABILITY,
|
|
9373
|
+
computer: COMPUTER_ABILITY,
|
|
9181
9374
|
docker: DOCKER_ABILITY,
|
|
9182
9375
|
github: GITHUB_ABILITY,
|
|
9183
9376
|
google: GOOGLE_ABILITY,
|
|
@@ -9222,6 +9415,14 @@ var HOOK_EXEC_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
|
|
|
9222
9415
|
// ../shared/src/replicas-config.ts
|
|
9223
9416
|
var REPLICAS_CONFIG_FILENAMES = ["replicas.json", "replicas.yaml", "replicas.yml"];
|
|
9224
9417
|
|
|
9418
|
+
// ../shared/src/engine/environment.ts
|
|
9419
|
+
var DESKTOP_NOVNC_PORT = 6080;
|
|
9420
|
+
function getDesktopViewerUrl(publicUrl) {
|
|
9421
|
+
return `${publicUrl.replace(/\/$/, "")}/`;
|
|
9422
|
+
}
|
|
9423
|
+
var DESKTOP_VIEWER_WIDTH = 1920;
|
|
9424
|
+
var DESKTOP_VIEWER_HEIGHT = 1080;
|
|
9425
|
+
|
|
9225
9426
|
// ../shared/src/engine/types.ts
|
|
9226
9427
|
var DEFAULT_CHAT_TITLES = {
|
|
9227
9428
|
claude: "Claude Code",
|
|
@@ -10746,7 +10947,7 @@ async function exchangeCodeForTokens(code, codeVerifier) {
|
|
|
10746
10947
|
};
|
|
10747
10948
|
}
|
|
10748
10949
|
function startCallbackServer(expectedState, codeVerifier) {
|
|
10749
|
-
return new Promise((
|
|
10950
|
+
return new Promise((resolve2, reject) => {
|
|
10750
10951
|
const server = http2.createServer(async (req, res) => {
|
|
10751
10952
|
try {
|
|
10752
10953
|
if (!req.url) {
|
|
@@ -10789,7 +10990,7 @@ function startCallbackServer(expectedState, codeVerifier) {
|
|
|
10789
10990
|
res.writeHead(302, { "Location": `${WEB_APP_URL2}/codex/oauth/success` });
|
|
10790
10991
|
res.end();
|
|
10791
10992
|
server.close();
|
|
10792
|
-
|
|
10993
|
+
resolve2(tokens);
|
|
10793
10994
|
} catch (tokenError) {
|
|
10794
10995
|
const errorMessage = encodeURIComponent(tokenError instanceof Error ? tokenError.message : "Unknown error");
|
|
10795
10996
|
res.writeHead(302, { "Location": `${WEB_APP_URL2}/codex/oauth/error?message=${errorMessage}` });
|
|
@@ -10823,7 +11024,7 @@ async function runCodexOAuthFlow() {
|
|
|
10823
11024
|
const state = generateState2();
|
|
10824
11025
|
const authUrl = buildAuthorizationUrl(pkce.codeChallenge, state);
|
|
10825
11026
|
const tokensPromise = startCallbackServer(state, pkce.codeVerifier);
|
|
10826
|
-
await new Promise((
|
|
11027
|
+
await new Promise((resolve2) => setTimeout(resolve2, 500));
|
|
10827
11028
|
console.log("If the browser does not open automatically, visit:");
|
|
10828
11029
|
console.log(authUrl);
|
|
10829
11030
|
console.log();
|
|
@@ -10912,10 +11113,10 @@ async function promptForAuthorizationCode(instruction) {
|
|
|
10912
11113
|
input: process.stdin,
|
|
10913
11114
|
output: process.stdout
|
|
10914
11115
|
});
|
|
10915
|
-
return new Promise((
|
|
11116
|
+
return new Promise((resolve2) => {
|
|
10916
11117
|
rl.question(instruction, (answer) => {
|
|
10917
11118
|
rl.close();
|
|
10918
|
-
|
|
11119
|
+
resolve2(answer.trim());
|
|
10919
11120
|
});
|
|
10920
11121
|
});
|
|
10921
11122
|
}
|
|
@@ -12274,20 +12475,31 @@ async function engineFetch(path6, options) {
|
|
|
12274
12475
|
}
|
|
12275
12476
|
|
|
12276
12477
|
// src/commands/preview.ts
|
|
12277
|
-
function
|
|
12478
|
+
function parsePreviewPort(port) {
|
|
12278
12479
|
const portNum = parseInt(port, 10);
|
|
12279
12480
|
if (isNaN(portNum) || portNum < 1 || portNum > 65535) {
|
|
12280
12481
|
throw new Error("Port must be a number between 1 and 65535");
|
|
12281
12482
|
}
|
|
12282
12483
|
return portNum;
|
|
12283
12484
|
}
|
|
12485
|
+
function createAgentPreview(port, authenticated) {
|
|
12486
|
+
return agentFetch("/v1/previews", {
|
|
12487
|
+
method: "POST",
|
|
12488
|
+
body: { port, authenticated }
|
|
12489
|
+
});
|
|
12490
|
+
}
|
|
12491
|
+
function listAgentPreviews() {
|
|
12492
|
+
return engineFetch("/previews");
|
|
12493
|
+
}
|
|
12494
|
+
function deleteAgentPreview(port) {
|
|
12495
|
+
return agentFetch(`/v1/previews/${port}`, {
|
|
12496
|
+
method: "DELETE"
|
|
12497
|
+
});
|
|
12498
|
+
}
|
|
12284
12499
|
async function previewCreateCommand(port, options) {
|
|
12285
|
-
const portNum =
|
|
12500
|
+
const portNum = parsePreviewPort(port);
|
|
12286
12501
|
if (isAgentMode()) {
|
|
12287
|
-
const result = await
|
|
12288
|
-
method: "POST",
|
|
12289
|
-
body: { port: portNum, authenticated: options.authenticated ?? false }
|
|
12290
|
-
});
|
|
12502
|
+
const result = await createAgentPreview(portNum, options.authenticated ?? false);
|
|
12291
12503
|
console.log(result.preview.publicUrl);
|
|
12292
12504
|
} else {
|
|
12293
12505
|
throw new Error("In human mode, use: replicas preview add <workspaceId> --port <port>");
|
|
@@ -12295,7 +12507,7 @@ async function previewCreateCommand(port, options) {
|
|
|
12295
12507
|
}
|
|
12296
12508
|
async function previewListCommand(workspaceId) {
|
|
12297
12509
|
if (isAgentMode()) {
|
|
12298
|
-
const result = await
|
|
12510
|
+
const result = await listAgentPreviews();
|
|
12299
12511
|
if (result.previews.length === 0) {
|
|
12300
12512
|
console.log("No active previews");
|
|
12301
12513
|
return;
|
|
@@ -12320,7 +12532,7 @@ async function previewListCommand(workspaceId) {
|
|
|
12320
12532
|
}
|
|
12321
12533
|
}
|
|
12322
12534
|
async function previewAddCommand(workspaceId, options) {
|
|
12323
|
-
const portNum =
|
|
12535
|
+
const portNum = parsePreviewPort(options.port);
|
|
12324
12536
|
const result = await orgAuthenticatedFetch(
|
|
12325
12537
|
`/v1/workspaces/${workspaceId}/previews`,
|
|
12326
12538
|
{
|
|
@@ -12331,14 +12543,12 @@ async function previewAddCommand(workspaceId, options) {
|
|
|
12331
12543
|
console.log(chalk18.green(`Preview created: ${result.preview.publicUrl}`));
|
|
12332
12544
|
}
|
|
12333
12545
|
async function previewDeleteCommand(port) {
|
|
12334
|
-
const portNum =
|
|
12335
|
-
await
|
|
12336
|
-
method: "DELETE"
|
|
12337
|
-
});
|
|
12546
|
+
const portNum = parsePreviewPort(port);
|
|
12547
|
+
await deleteAgentPreview(portNum);
|
|
12338
12548
|
console.log(`Preview deleted on port ${portNum}`);
|
|
12339
12549
|
}
|
|
12340
12550
|
async function previewRemoveCommand(workspaceId, options) {
|
|
12341
|
-
const portNum =
|
|
12551
|
+
const portNum = parsePreviewPort(options.port);
|
|
12342
12552
|
await orgAuthenticatedFetch(
|
|
12343
12553
|
`/v1/workspaces/${workspaceId}/previews/${portNum}`,
|
|
12344
12554
|
{ method: "DELETE" }
|
|
@@ -12444,8 +12654,289 @@ async function mediaListCommand(options) {
|
|
|
12444
12654
|
}
|
|
12445
12655
|
}
|
|
12446
12656
|
|
|
12447
|
-
// src/commands/
|
|
12657
|
+
// src/commands/computer.ts
|
|
12658
|
+
import { spawn as spawn3, spawnSync } from "child_process";
|
|
12659
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
12660
|
+
import { dirname, isAbsolute, resolve } from "path";
|
|
12448
12661
|
import chalk19 from "chalk";
|
|
12662
|
+
var STATE_DIR = process.env.REPLICAS_DESKTOP_STATE_DIR || "/tmp/replicas-computer";
|
|
12663
|
+
var DEFAULT_DISPLAY = process.env.REPLICAS_DESKTOP_DISPLAY || ":99";
|
|
12664
|
+
var NOVNC_PORT = process.env.REPLICAS_DESKTOP_NOVNC_PORT ? parseInt(process.env.REPLICAS_DESKTOP_NOVNC_PORT, 10) : DESKTOP_NOVNC_PORT;
|
|
12665
|
+
var SERVICES_SCRIPT = "/usr/local/bin/replicas-start-desktop-services";
|
|
12666
|
+
function fail(msg) {
|
|
12667
|
+
throw new Error(msg);
|
|
12668
|
+
}
|
|
12669
|
+
function ensureServicesRunning() {
|
|
12670
|
+
if (!existsSync(SERVICES_SCRIPT)) {
|
|
12671
|
+
fail(
|
|
12672
|
+
`Desktop services script missing at ${SERVICES_SCRIPT}. The workspace image is out of date \u2014 Xvfb / openbox / x11vnc / websockify must be installed and \`replicas-start-desktop-services\` baked in.`
|
|
12673
|
+
);
|
|
12674
|
+
}
|
|
12675
|
+
const r = spawnSync("bash", [SERVICES_SCRIPT], { stdio: "pipe" });
|
|
12676
|
+
if (r.status !== 0) {
|
|
12677
|
+
fail(`Failed to start desktop services: ${r.stderr?.toString() || "unknown error"}`);
|
|
12678
|
+
}
|
|
12679
|
+
}
|
|
12680
|
+
function withDisplay(env = process.env) {
|
|
12681
|
+
return { ...env, DISPLAY: DEFAULT_DISPLAY };
|
|
12682
|
+
}
|
|
12683
|
+
function runDisplayCmd(bin, args) {
|
|
12684
|
+
ensureServicesRunning();
|
|
12685
|
+
const r = spawnSync(bin, args, { env: withDisplay(), stdio: "pipe" });
|
|
12686
|
+
if (r.status !== 0) {
|
|
12687
|
+
fail(`${bin} ${args.join(" ")} failed: ${r.stderr?.toString().trim() || `exit ${r.status}`}`);
|
|
12688
|
+
}
|
|
12689
|
+
return r.stdout?.toString() ?? "";
|
|
12690
|
+
}
|
|
12691
|
+
function parseCoord(value, label) {
|
|
12692
|
+
const n = Number.parseInt(value, 10);
|
|
12693
|
+
if (!Number.isFinite(n)) fail(`${label} must be an integer (got "${value}")`);
|
|
12694
|
+
return n;
|
|
12695
|
+
}
|
|
12696
|
+
function resolvePath(p) {
|
|
12697
|
+
return isAbsolute(p) ? p : resolve(process.cwd(), p);
|
|
12698
|
+
}
|
|
12699
|
+
async function computerStartCommand(options) {
|
|
12700
|
+
if (options.display) process.env.REPLICAS_DESKTOP_DISPLAY = options.display;
|
|
12701
|
+
if (options.size) {
|
|
12702
|
+
const m = /^(\d+)x(\d+)$/.exec(options.size);
|
|
12703
|
+
if (!m) fail(`--size must be WIDTHxHEIGHT (got "${options.size}")`);
|
|
12704
|
+
process.env.REPLICAS_DESKTOP_WIDTH = m[1];
|
|
12705
|
+
process.env.REPLICAS_DESKTOP_HEIGHT = m[2];
|
|
12706
|
+
}
|
|
12707
|
+
ensureServicesRunning();
|
|
12708
|
+
const port = options.port ? parsePreviewPort(options.port) : NOVNC_PORT;
|
|
12709
|
+
let publicUrl;
|
|
12710
|
+
try {
|
|
12711
|
+
const result = await createAgentPreview(port, true);
|
|
12712
|
+
publicUrl = result.preview.publicUrl;
|
|
12713
|
+
} catch (err) {
|
|
12714
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
12715
|
+
if (!/409/.test(msg)) throw err;
|
|
12716
|
+
const list = await listAgentPreviews();
|
|
12717
|
+
const existing = list.previews.find((p) => p.port === port);
|
|
12718
|
+
if (!existing) throw err;
|
|
12719
|
+
publicUrl = existing.publicUrl;
|
|
12720
|
+
}
|
|
12721
|
+
const viewerUrl = getDesktopViewerUrl(publicUrl);
|
|
12722
|
+
mkdirSync(STATE_DIR, { recursive: true });
|
|
12723
|
+
writeFileSync(
|
|
12724
|
+
`${STATE_DIR}/preview.json`,
|
|
12725
|
+
JSON.stringify({ port, publicUrl, viewerUrl }, null, 2)
|
|
12726
|
+
);
|
|
12727
|
+
console.log(viewerUrl);
|
|
12728
|
+
console.error(chalk19.dim(`Desktop preview ready (port ${port}, authenticated).`));
|
|
12729
|
+
console.error(chalk19.dim(`The Replicas dashboard will show a "Desktop" tab while this preview is live.`));
|
|
12730
|
+
}
|
|
12731
|
+
async function computerStopCommand(options) {
|
|
12732
|
+
const port = options.port ? parsePreviewPort(options.port) : NOVNC_PORT;
|
|
12733
|
+
await deleteAgentPreview(port);
|
|
12734
|
+
rmSync(`${STATE_DIR}/preview.json`, { force: true });
|
|
12735
|
+
console.log(`Desktop preview stopped on port ${port}`);
|
|
12736
|
+
}
|
|
12737
|
+
async function computerStatusCommand() {
|
|
12738
|
+
const procs = ["Xvfb", "openbox", "tint2", "x11vnc", "websockify"];
|
|
12739
|
+
for (const p of procs) {
|
|
12740
|
+
const r = spawnSync("pgrep", ["-af", p], { stdio: "pipe" });
|
|
12741
|
+
const running = r.status === 0 && !!r.stdout?.toString().trim();
|
|
12742
|
+
console.log(` ${running ? chalk19.green("\u25CF") : chalk19.red("\u25CB")} ${p}`);
|
|
12743
|
+
}
|
|
12744
|
+
const previewFile = `${STATE_DIR}/preview.json`;
|
|
12745
|
+
if (existsSync(previewFile)) {
|
|
12746
|
+
try {
|
|
12747
|
+
const meta = JSON.parse(readFileSync(previewFile, "utf8"));
|
|
12748
|
+
console.log(` ${chalk19.cyan("preview")}: ${meta.viewerUrl}`);
|
|
12749
|
+
} catch {
|
|
12750
|
+
}
|
|
12751
|
+
} else {
|
|
12752
|
+
console.log(` ${chalk19.dim("preview: not started (run `replicas computer start`)")}`);
|
|
12753
|
+
}
|
|
12754
|
+
}
|
|
12755
|
+
async function computerScreenshotCommand(path6) {
|
|
12756
|
+
const target = resolvePath(path6);
|
|
12757
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
12758
|
+
runDisplayCmd("scrot", ["-o", target]);
|
|
12759
|
+
console.log(target);
|
|
12760
|
+
}
|
|
12761
|
+
async function computerClickCommand(xStr, yStr, options) {
|
|
12762
|
+
const x = parseCoord(xStr, "x");
|
|
12763
|
+
const y = parseCoord(yStr, "y");
|
|
12764
|
+
const button = options.button ?? "1";
|
|
12765
|
+
const args = ["mousemove", "--sync", String(x), String(y)];
|
|
12766
|
+
if (options.modifiers) {
|
|
12767
|
+
for (const mod of options.modifiers.split("+")) {
|
|
12768
|
+
args.push("keydown", mod);
|
|
12769
|
+
}
|
|
12770
|
+
}
|
|
12771
|
+
if (options.double) {
|
|
12772
|
+
args.push("click", "--repeat", "2", "--delay", "50", button);
|
|
12773
|
+
} else {
|
|
12774
|
+
args.push("click", button);
|
|
12775
|
+
}
|
|
12776
|
+
if (options.modifiers) {
|
|
12777
|
+
for (const mod of options.modifiers.split("+").reverse()) {
|
|
12778
|
+
args.push("keyup", mod);
|
|
12779
|
+
}
|
|
12780
|
+
}
|
|
12781
|
+
runDisplayCmd("xdotool", args);
|
|
12782
|
+
console.log(`clicked ${button === "1" ? "left" : button === "2" ? "middle" : button === "3" ? "right" : `button ${button}`} at (${x},${y})${options.double ? " x2" : ""}`);
|
|
12783
|
+
}
|
|
12784
|
+
async function computerMoveCommand(xStr, yStr) {
|
|
12785
|
+
const x = parseCoord(xStr, "x");
|
|
12786
|
+
const y = parseCoord(yStr, "y");
|
|
12787
|
+
runDisplayCmd("xdotool", ["mousemove", "--sync", String(x), String(y)]);
|
|
12788
|
+
console.log(`moved to (${x},${y})`);
|
|
12789
|
+
}
|
|
12790
|
+
async function computerTypeCommand(text, options) {
|
|
12791
|
+
const delay = options.delay ? parseCoord(options.delay, "--delay") : 12;
|
|
12792
|
+
runDisplayCmd("xdotool", ["type", "--delay", String(delay), "--", text]);
|
|
12793
|
+
console.log(`typed ${text.length} char${text.length === 1 ? "" : "s"}`);
|
|
12794
|
+
}
|
|
12795
|
+
async function computerKeyCommand(combo) {
|
|
12796
|
+
runDisplayCmd("xdotool", ["key", "--", combo]);
|
|
12797
|
+
console.log(`pressed ${combo}`);
|
|
12798
|
+
}
|
|
12799
|
+
async function computerScrollCommand(direction, options) {
|
|
12800
|
+
const dir = direction.toLowerCase();
|
|
12801
|
+
const buttonMap = { up: "4", down: "5", left: "6", right: "7" };
|
|
12802
|
+
const button = buttonMap[dir];
|
|
12803
|
+
if (!button) fail(`direction must be one of up|down|left|right (got "${direction}")`);
|
|
12804
|
+
const amount = options.amount ? parseCoord(options.amount, "--amount") : 3;
|
|
12805
|
+
const args = [];
|
|
12806
|
+
if (options.x && options.y) {
|
|
12807
|
+
args.push("mousemove", "--sync", options.x, options.y);
|
|
12808
|
+
}
|
|
12809
|
+
args.push("click", "--repeat", String(amount), "--delay", "30", button);
|
|
12810
|
+
runDisplayCmd("xdotool", args);
|
|
12811
|
+
console.log(`scrolled ${dir} x${amount}`);
|
|
12812
|
+
}
|
|
12813
|
+
async function computerDragCommand(fx, fy, tx, ty) {
|
|
12814
|
+
const fromX = parseCoord(fx, "fromX");
|
|
12815
|
+
const fromY = parseCoord(fy, "fromY");
|
|
12816
|
+
const toX = parseCoord(tx, "toX");
|
|
12817
|
+
const toY = parseCoord(ty, "toY");
|
|
12818
|
+
runDisplayCmd("xdotool", [
|
|
12819
|
+
"mousemove",
|
|
12820
|
+
"--sync",
|
|
12821
|
+
String(fromX),
|
|
12822
|
+
String(fromY),
|
|
12823
|
+
"mousedown",
|
|
12824
|
+
"1",
|
|
12825
|
+
"mousemove",
|
|
12826
|
+
"--sync",
|
|
12827
|
+
String(toX),
|
|
12828
|
+
String(toY),
|
|
12829
|
+
"mouseup",
|
|
12830
|
+
"1"
|
|
12831
|
+
]);
|
|
12832
|
+
console.log(`dragged (${fromX},${fromY}) -> (${toX},${toY})`);
|
|
12833
|
+
}
|
|
12834
|
+
var APP_ALIASES = {
|
|
12835
|
+
chrome: ["google-chrome", "--no-first-run", "--no-default-browser-check", "--start-maximized", "--user-data-dir=/tmp/replicas-computer/chrome-profile"],
|
|
12836
|
+
chromium: ["chromium", "--no-first-run", "--no-default-browser-check"],
|
|
12837
|
+
firefox: ["firefox"],
|
|
12838
|
+
terminal: ["xfce4-terminal"],
|
|
12839
|
+
xterm: ["xterm"],
|
|
12840
|
+
notepad: ["mousepad"],
|
|
12841
|
+
editor: ["mousepad"],
|
|
12842
|
+
files: ["thunar"],
|
|
12843
|
+
filemanager: ["thunar"]
|
|
12844
|
+
};
|
|
12845
|
+
async function computerLaunchCommand(app, args) {
|
|
12846
|
+
ensureServicesRunning();
|
|
12847
|
+
const baseArgs = APP_ALIASES[app] ?? [app];
|
|
12848
|
+
const bin = baseArgs[0];
|
|
12849
|
+
const fullArgs = [...baseArgs.slice(1), ...args];
|
|
12850
|
+
const child = spawn3(bin, fullArgs, {
|
|
12851
|
+
env: withDisplay(),
|
|
12852
|
+
detached: true,
|
|
12853
|
+
stdio: "ignore"
|
|
12854
|
+
});
|
|
12855
|
+
child.unref();
|
|
12856
|
+
console.log(`launched ${bin} (pid ${child.pid})`);
|
|
12857
|
+
}
|
|
12858
|
+
var RECORD_PID_FILE = `${STATE_DIR}/ffmpeg.pid`;
|
|
12859
|
+
async function computerRecordStartCommand(path6, options) {
|
|
12860
|
+
ensureServicesRunning();
|
|
12861
|
+
if (existsSync(RECORD_PID_FILE)) {
|
|
12862
|
+
const pid = parseInt(readFileSync(RECORD_PID_FILE, "utf8").trim(), 10);
|
|
12863
|
+
if (Number.isFinite(pid)) {
|
|
12864
|
+
let alive = false;
|
|
12865
|
+
try {
|
|
12866
|
+
process.kill(pid, 0);
|
|
12867
|
+
alive = true;
|
|
12868
|
+
} catch {
|
|
12869
|
+
}
|
|
12870
|
+
if (alive) fail(`recording already in progress (pid ${pid}). run \`replicas computer record stop\` first.`);
|
|
12871
|
+
}
|
|
12872
|
+
}
|
|
12873
|
+
const target = resolvePath(path6);
|
|
12874
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
12875
|
+
const fps = options.fps ? parseCoord(options.fps, "--fps") : 60;
|
|
12876
|
+
const width = process.env.REPLICAS_DESKTOP_WIDTH || String(DESKTOP_VIEWER_WIDTH);
|
|
12877
|
+
const height = process.env.REPLICAS_DESKTOP_HEIGHT || String(DESKTOP_VIEWER_HEIGHT);
|
|
12878
|
+
const child = spawn3("ffmpeg", [
|
|
12879
|
+
"-y",
|
|
12880
|
+
"-hide_banner",
|
|
12881
|
+
"-loglevel",
|
|
12882
|
+
"warning",
|
|
12883
|
+
"-f",
|
|
12884
|
+
"x11grab",
|
|
12885
|
+
"-framerate",
|
|
12886
|
+
String(fps),
|
|
12887
|
+
"-video_size",
|
|
12888
|
+
`${width}x${height}`,
|
|
12889
|
+
"-draw_mouse",
|
|
12890
|
+
"1",
|
|
12891
|
+
"-i",
|
|
12892
|
+
DEFAULT_DISPLAY,
|
|
12893
|
+
"-c:v",
|
|
12894
|
+
"libx264",
|
|
12895
|
+
"-preset",
|
|
12896
|
+
"ultrafast",
|
|
12897
|
+
"-tune",
|
|
12898
|
+
"zerolatency",
|
|
12899
|
+
"-crf",
|
|
12900
|
+
"23",
|
|
12901
|
+
"-pix_fmt",
|
|
12902
|
+
"yuv420p",
|
|
12903
|
+
"-movflags",
|
|
12904
|
+
"+faststart+frag_keyframe+empty_moov",
|
|
12905
|
+
target
|
|
12906
|
+
], { detached: true, stdio: "ignore" });
|
|
12907
|
+
child.unref();
|
|
12908
|
+
if (!child.pid) fail("failed to launch ffmpeg");
|
|
12909
|
+
mkdirSync(STATE_DIR, { recursive: true });
|
|
12910
|
+
writeFileSync(RECORD_PID_FILE, String(child.pid));
|
|
12911
|
+
writeFileSync(`${STATE_DIR}/recording-path.txt`, target);
|
|
12912
|
+
console.log(target);
|
|
12913
|
+
}
|
|
12914
|
+
async function computerRecordStopCommand() {
|
|
12915
|
+
if (!existsSync(RECORD_PID_FILE)) fail("no recording in progress");
|
|
12916
|
+
const pid = parseInt(readFileSync(RECORD_PID_FILE, "utf8").trim(), 10);
|
|
12917
|
+
if (!Number.isFinite(pid)) fail("invalid recording pidfile");
|
|
12918
|
+
try {
|
|
12919
|
+
process.kill(pid, "SIGINT");
|
|
12920
|
+
} catch {
|
|
12921
|
+
}
|
|
12922
|
+
for (let i = 0; i < 30; i++) {
|
|
12923
|
+
try {
|
|
12924
|
+
process.kill(pid, 0);
|
|
12925
|
+
} catch {
|
|
12926
|
+
break;
|
|
12927
|
+
}
|
|
12928
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
12929
|
+
}
|
|
12930
|
+
rmSync(RECORD_PID_FILE, { force: true });
|
|
12931
|
+
const pathFile = `${STATE_DIR}/recording-path.txt`;
|
|
12932
|
+
if (existsSync(pathFile)) {
|
|
12933
|
+
console.log(readFileSync(pathFile, "utf8").trim());
|
|
12934
|
+
rmSync(pathFile, { force: true });
|
|
12935
|
+
}
|
|
12936
|
+
}
|
|
12937
|
+
|
|
12938
|
+
// src/commands/interactive.ts
|
|
12939
|
+
import chalk20 from "chalk";
|
|
12449
12940
|
|
|
12450
12941
|
// src/interactive/index.tsx
|
|
12451
12942
|
import { createCliRenderer } from "@opentui/core";
|
|
@@ -12898,7 +13389,7 @@ function useWorkspaceEvents(workspaceId, enabled = true) {
|
|
|
12898
13389
|
while (!cancelled) {
|
|
12899
13390
|
await connect();
|
|
12900
13391
|
if (cancelled) break;
|
|
12901
|
-
await new Promise((
|
|
13392
|
+
await new Promise((resolve2) => setTimeout(resolve2, 1e3));
|
|
12902
13393
|
}
|
|
12903
13394
|
};
|
|
12904
13395
|
run().catch(() => setConnected(false));
|
|
@@ -15565,13 +16056,13 @@ async function interactiveCommand() {
|
|
|
15565
16056
|
'No organization selected. Please run "replicas org switch" to select an organization.'
|
|
15566
16057
|
);
|
|
15567
16058
|
}
|
|
15568
|
-
console.log(
|
|
16059
|
+
console.log(chalk20.gray("Starting interactive mode..."));
|
|
15569
16060
|
await launchInteractive();
|
|
15570
16061
|
}
|
|
15571
16062
|
|
|
15572
16063
|
// src/commands/environment.ts
|
|
15573
16064
|
import fs5 from "fs";
|
|
15574
|
-
import
|
|
16065
|
+
import chalk21 from "chalk";
|
|
15575
16066
|
import prompts5 from "prompts";
|
|
15576
16067
|
var UUID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
15577
16068
|
function maskValue(value) {
|
|
@@ -15584,38 +16075,38 @@ async function resolveEnvironmentId(input) {
|
|
|
15584
16075
|
const response = await orgAuthenticatedFetch("/v1/environments");
|
|
15585
16076
|
const env = response.environments.find((e) => e.name === input);
|
|
15586
16077
|
if (!env) {
|
|
15587
|
-
console.log(
|
|
16078
|
+
console.log(chalk21.red(`Environment not found: ${input}`));
|
|
15588
16079
|
const available = response.environments.map((e) => e.name).join(", ");
|
|
15589
|
-
console.log(
|
|
16080
|
+
console.log(chalk21.gray(`Available: ${available || "(none)"}`));
|
|
15590
16081
|
process.exit(1);
|
|
15591
16082
|
}
|
|
15592
16083
|
return env.id;
|
|
15593
16084
|
}
|
|
15594
16085
|
function printEnvironment(env) {
|
|
15595
|
-
console.log(
|
|
15596
|
-
console.log(
|
|
16086
|
+
console.log(chalk21.white(` ${env.name}${env.is_global ? chalk21.gray(" (global)") : ""}`));
|
|
16087
|
+
console.log(chalk21.gray(` ID: ${env.id}`));
|
|
15597
16088
|
if (env.description) {
|
|
15598
|
-
console.log(
|
|
16089
|
+
console.log(chalk21.gray(` Description: ${env.description}`));
|
|
15599
16090
|
}
|
|
15600
16091
|
if (env.repository_id) {
|
|
15601
|
-
console.log(
|
|
16092
|
+
console.log(chalk21.gray(` Repository: ${env.repository_id}`));
|
|
15602
16093
|
} else if (env.repository_set_id) {
|
|
15603
|
-
console.log(
|
|
16094
|
+
console.log(chalk21.gray(` Repository Set: ${env.repository_set_id}`));
|
|
15604
16095
|
}
|
|
15605
16096
|
if (env.variable_count !== void 0) {
|
|
15606
|
-
console.log(
|
|
16097
|
+
console.log(chalk21.gray(` Variables: ${env.variable_count}, Files: ${env.file_count ?? 0}, Skills: ${env.skill_count ?? 0}, MCPs: ${env.mcp_count ?? 0}`));
|
|
15607
16098
|
}
|
|
15608
|
-
console.log(
|
|
16099
|
+
console.log(chalk21.gray(` Updated: ${formatDate2(env.updated_at)}`));
|
|
15609
16100
|
console.log();
|
|
15610
16101
|
}
|
|
15611
16102
|
async function environmentListCommand() {
|
|
15612
16103
|
ensureOrgApiAuthenticated();
|
|
15613
16104
|
const response = await orgAuthenticatedFetch("/v1/environments");
|
|
15614
16105
|
if (response.environments.length === 0) {
|
|
15615
|
-
console.log(
|
|
16106
|
+
console.log(chalk21.yellow("\nNo environments found.\n"));
|
|
15616
16107
|
return;
|
|
15617
16108
|
}
|
|
15618
|
-
console.log(
|
|
16109
|
+
console.log(chalk21.green(`
|
|
15619
16110
|
Environments (${response.environments.length}):
|
|
15620
16111
|
`));
|
|
15621
16112
|
for (const env of response.environments) {
|
|
@@ -15626,7 +16117,7 @@ async function environmentGetCommand(idOrName) {
|
|
|
15626
16117
|
ensureOrgApiAuthenticated();
|
|
15627
16118
|
const id = await resolveEnvironmentId(idOrName);
|
|
15628
16119
|
const response = await orgAuthenticatedFetch(`/v1/environments/${id}`);
|
|
15629
|
-
console.log(
|
|
16120
|
+
console.log(chalk21.green(`
|
|
15630
16121
|
Environment: ${response.environment.name}
|
|
15631
16122
|
`));
|
|
15632
16123
|
printEnvironment(response.environment);
|
|
@@ -15642,7 +16133,7 @@ async function environmentCreateCommand(name, options) {
|
|
|
15642
16133
|
validate: (v) => v.trim() ? true : "Name is required"
|
|
15643
16134
|
});
|
|
15644
16135
|
if (!r.name) {
|
|
15645
|
-
console.log(
|
|
16136
|
+
console.log(chalk21.yellow("\nCancelled."));
|
|
15646
16137
|
return;
|
|
15647
16138
|
}
|
|
15648
16139
|
envName = r.name;
|
|
@@ -15655,8 +16146,8 @@ async function environmentCreateCommand(name, options) {
|
|
|
15655
16146
|
const repos2 = await orgAuthenticatedFetch("/v1/repositories");
|
|
15656
16147
|
const repo = repos2.repositories.find((r) => r.name === options.repository);
|
|
15657
16148
|
if (!repo) {
|
|
15658
|
-
console.log(
|
|
15659
|
-
console.log(
|
|
16149
|
+
console.log(chalk21.red(`Repository not found: ${options.repository}`));
|
|
16150
|
+
console.log(chalk21.gray(`Available: ${repos2.repositories.map((r) => r.name).join(", ")}`));
|
|
15660
16151
|
process.exit(1);
|
|
15661
16152
|
}
|
|
15662
16153
|
repositoryId = repo.id;
|
|
@@ -15686,9 +16177,9 @@ async function environmentCreateCommand(name, options) {
|
|
|
15686
16177
|
method: "POST",
|
|
15687
16178
|
body
|
|
15688
16179
|
});
|
|
15689
|
-
console.log(
|
|
16180
|
+
console.log(chalk21.green(`
|
|
15690
16181
|
Created environment: ${response.environment.name}`));
|
|
15691
|
-
console.log(
|
|
16182
|
+
console.log(chalk21.gray(` ID: ${response.environment.id}
|
|
15692
16183
|
`));
|
|
15693
16184
|
}
|
|
15694
16185
|
async function environmentEditCommand(idOrName, options) {
|
|
@@ -15707,21 +16198,21 @@ async function environmentEditCommand(idOrName, options) {
|
|
|
15707
16198
|
const repos2 = await orgAuthenticatedFetch("/v1/repositories");
|
|
15708
16199
|
const repo = repos2.repositories.find((r) => r.name === options.repository);
|
|
15709
16200
|
if (!repo) {
|
|
15710
|
-
console.log(
|
|
16201
|
+
console.log(chalk21.red(`Repository not found: ${options.repository}`));
|
|
15711
16202
|
process.exit(1);
|
|
15712
16203
|
}
|
|
15713
16204
|
body.repository_id = repo.id;
|
|
15714
16205
|
}
|
|
15715
16206
|
}
|
|
15716
16207
|
if (Object.keys(body).length === 0) {
|
|
15717
|
-
console.log(
|
|
16208
|
+
console.log(chalk21.yellow("\nNo changes specified. Pass --name, --description, --repository, or --system-prompt."));
|
|
15718
16209
|
return;
|
|
15719
16210
|
}
|
|
15720
16211
|
const response = await orgAuthenticatedFetch(`/v1/environments/${id}`, {
|
|
15721
16212
|
method: "PATCH",
|
|
15722
16213
|
body
|
|
15723
16214
|
});
|
|
15724
|
-
console.log(
|
|
16215
|
+
console.log(chalk21.green(`
|
|
15725
16216
|
Updated environment: ${response.environment.name}
|
|
15726
16217
|
`));
|
|
15727
16218
|
}
|
|
@@ -15736,20 +16227,20 @@ async function environmentDeleteCommand(idOrName, options) {
|
|
|
15736
16227
|
initial: false
|
|
15737
16228
|
});
|
|
15738
16229
|
if (!r.confirm) {
|
|
15739
|
-
console.log(
|
|
16230
|
+
console.log(chalk21.yellow("\nCancelled."));
|
|
15740
16231
|
return;
|
|
15741
16232
|
}
|
|
15742
16233
|
}
|
|
15743
16234
|
await orgAuthenticatedFetch(`/v1/environments/${id}`, { method: "DELETE" });
|
|
15744
|
-
console.log(
|
|
16235
|
+
console.log(chalk21.green(`
|
|
15745
16236
|
Deleted environment ${idOrName}.
|
|
15746
16237
|
`));
|
|
15747
16238
|
}
|
|
15748
16239
|
function printVariable(v, reveal) {
|
|
15749
|
-
console.log(
|
|
15750
|
-
console.log(
|
|
15751
|
-
console.log(
|
|
15752
|
-
console.log(
|
|
16240
|
+
console.log(chalk21.white(` ${v.key}`));
|
|
16241
|
+
console.log(chalk21.gray(` ID: ${v.id}`));
|
|
16242
|
+
console.log(chalk21.gray(` Value: ${reveal ? v.value : maskValue(v.value)}`));
|
|
16243
|
+
console.log(chalk21.gray(` Updated: ${formatDate2(v.updated_at)}`));
|
|
15753
16244
|
console.log();
|
|
15754
16245
|
}
|
|
15755
16246
|
async function envVarsListCommand(envIdOrName, options) {
|
|
@@ -15759,14 +16250,14 @@ async function envVarsListCommand(envIdOrName, options) {
|
|
|
15759
16250
|
`/v1/environments/${id}/variables`
|
|
15760
16251
|
);
|
|
15761
16252
|
if (response.environment_variables.length === 0) {
|
|
15762
|
-
console.log(
|
|
16253
|
+
console.log(chalk21.yellow("\nNo variables.\n"));
|
|
15763
16254
|
return;
|
|
15764
16255
|
}
|
|
15765
|
-
console.log(
|
|
16256
|
+
console.log(chalk21.green(`
|
|
15766
16257
|
Variables (${response.environment_variables.length}):
|
|
15767
16258
|
`));
|
|
15768
16259
|
if (!options.reveal) {
|
|
15769
|
-
console.log(
|
|
16260
|
+
console.log(chalk21.gray(" Values are masked. Pass --reveal to show full values.\n"));
|
|
15770
16261
|
}
|
|
15771
16262
|
for (const v of response.environment_variables) printVariable(v, !!options.reveal);
|
|
15772
16263
|
}
|
|
@@ -15783,7 +16274,7 @@ async function envVarsSetCommand(envIdOrName, key, value) {
|
|
|
15783
16274
|
`/v1/environments/${id}/variables/${match.id}`,
|
|
15784
16275
|
{ method: "PATCH", body: body2 }
|
|
15785
16276
|
);
|
|
15786
|
-
console.log(
|
|
16277
|
+
console.log(chalk21.green(`
|
|
15787
16278
|
Updated variable ${response2.environment_variable.key}.
|
|
15788
16279
|
`));
|
|
15789
16280
|
return;
|
|
@@ -15797,7 +16288,7 @@ Updated variable ${response2.environment_variable.key}.
|
|
|
15797
16288
|
`/v1/environments/${id}/variables`,
|
|
15798
16289
|
{ method: "POST", body }
|
|
15799
16290
|
);
|
|
15800
|
-
console.log(
|
|
16291
|
+
console.log(chalk21.green(`
|
|
15801
16292
|
Created variable ${response.environment_variable.key}.
|
|
15802
16293
|
`));
|
|
15803
16294
|
}
|
|
@@ -15811,7 +16302,7 @@ async function envVarsDeleteCommand(envIdOrName, keyOrId, options) {
|
|
|
15811
16302
|
);
|
|
15812
16303
|
const match = existing.environment_variables.find((v) => v.key === keyOrId);
|
|
15813
16304
|
if (!match) {
|
|
15814
|
-
console.log(
|
|
16305
|
+
console.log(chalk21.red(`Variable not found: ${keyOrId}`));
|
|
15815
16306
|
process.exit(1);
|
|
15816
16307
|
}
|
|
15817
16308
|
variableId = match.id;
|
|
@@ -15824,23 +16315,23 @@ async function envVarsDeleteCommand(envIdOrName, keyOrId, options) {
|
|
|
15824
16315
|
initial: false
|
|
15825
16316
|
});
|
|
15826
16317
|
if (!r.confirm) {
|
|
15827
|
-
console.log(
|
|
16318
|
+
console.log(chalk21.yellow("\nCancelled."));
|
|
15828
16319
|
return;
|
|
15829
16320
|
}
|
|
15830
16321
|
}
|
|
15831
16322
|
await orgAuthenticatedFetch(`/v1/environments/${id}/variables/${variableId}`, {
|
|
15832
16323
|
method: "DELETE"
|
|
15833
16324
|
});
|
|
15834
|
-
console.log(
|
|
16325
|
+
console.log(chalk21.green(`
|
|
15835
16326
|
Deleted variable ${keyOrId}.
|
|
15836
16327
|
`));
|
|
15837
16328
|
}
|
|
15838
16329
|
function printFile(f) {
|
|
15839
|
-
console.log(
|
|
15840
|
-
console.log(
|
|
15841
|
-
console.log(
|
|
15842
|
-
console.log(
|
|
15843
|
-
console.log(
|
|
16330
|
+
console.log(chalk21.white(` ${f.path}`));
|
|
16331
|
+
console.log(chalk21.gray(` ID: ${f.id}`));
|
|
16332
|
+
console.log(chalk21.gray(` Name: ${f.name}`));
|
|
16333
|
+
console.log(chalk21.gray(` Size: ${f.content.length} bytes`));
|
|
16334
|
+
console.log(chalk21.gray(` Updated: ${formatDate2(f.updated_at)}`));
|
|
15844
16335
|
console.log();
|
|
15845
16336
|
}
|
|
15846
16337
|
async function envFilesListCommand(envIdOrName) {
|
|
@@ -15850,10 +16341,10 @@ async function envFilesListCommand(envIdOrName) {
|
|
|
15850
16341
|
`/v1/environments/${id}/files`
|
|
15851
16342
|
);
|
|
15852
16343
|
if (response.environment_files.length === 0) {
|
|
15853
|
-
console.log(
|
|
16344
|
+
console.log(chalk21.yellow("\nNo files.\n"));
|
|
15854
16345
|
return;
|
|
15855
16346
|
}
|
|
15856
|
-
console.log(
|
|
16347
|
+
console.log(chalk21.green(`
|
|
15857
16348
|
Files (${response.environment_files.length}):
|
|
15858
16349
|
`));
|
|
15859
16350
|
for (const f of response.environment_files) printFile(f);
|
|
@@ -15884,7 +16375,7 @@ async function envFilesSetCommand(envIdOrName, destinationPath, options) {
|
|
|
15884
16375
|
`/v1/environments/${id}/files/${match.id}`,
|
|
15885
16376
|
{ method: "PATCH", body: body2 }
|
|
15886
16377
|
);
|
|
15887
|
-
console.log(
|
|
16378
|
+
console.log(chalk21.green(`
|
|
15888
16379
|
Updated file ${response2.environment_file.path}.
|
|
15889
16380
|
`));
|
|
15890
16381
|
return;
|
|
@@ -15899,7 +16390,7 @@ Updated file ${response2.environment_file.path}.
|
|
|
15899
16390
|
`/v1/environments/${id}/files`,
|
|
15900
16391
|
{ method: "POST", body }
|
|
15901
16392
|
);
|
|
15902
|
-
console.log(
|
|
16393
|
+
console.log(chalk21.green(`
|
|
15903
16394
|
Created file ${response.environment_file.path}.
|
|
15904
16395
|
`));
|
|
15905
16396
|
}
|
|
@@ -15913,7 +16404,7 @@ async function envFilesDeleteCommand(envIdOrName, pathOrId, options) {
|
|
|
15913
16404
|
);
|
|
15914
16405
|
const match = existing.environment_files.find((f) => f.path === pathOrId);
|
|
15915
16406
|
if (!match) {
|
|
15916
|
-
console.log(
|
|
16407
|
+
console.log(chalk21.red(`File not found: ${pathOrId}`));
|
|
15917
16408
|
process.exit(1);
|
|
15918
16409
|
}
|
|
15919
16410
|
fileId = match.id;
|
|
@@ -15926,20 +16417,20 @@ async function envFilesDeleteCommand(envIdOrName, pathOrId, options) {
|
|
|
15926
16417
|
initial: false
|
|
15927
16418
|
});
|
|
15928
16419
|
if (!r.confirm) {
|
|
15929
|
-
console.log(
|
|
16420
|
+
console.log(chalk21.yellow("\nCancelled."));
|
|
15930
16421
|
return;
|
|
15931
16422
|
}
|
|
15932
16423
|
}
|
|
15933
16424
|
await orgAuthenticatedFetch(`/v1/environments/${id}/files/${fileId}`, {
|
|
15934
16425
|
method: "DELETE"
|
|
15935
16426
|
});
|
|
15936
|
-
console.log(
|
|
16427
|
+
console.log(chalk21.green(`
|
|
15937
16428
|
Deleted file ${pathOrId}.
|
|
15938
16429
|
`));
|
|
15939
16430
|
}
|
|
15940
16431
|
|
|
15941
16432
|
// src/index.ts
|
|
15942
|
-
var CLI_VERSION = "0.2.
|
|
16433
|
+
var CLI_VERSION = "0.2.184";
|
|
15943
16434
|
function parseBooleanOption(value) {
|
|
15944
16435
|
if (value === "true") return true;
|
|
15945
16436
|
if (value === "false") return false;
|
|
@@ -15952,7 +16443,7 @@ program.command("login").description("Authenticate with your Replicas account").
|
|
|
15952
16443
|
await loginCommand();
|
|
15953
16444
|
} catch (error) {
|
|
15954
16445
|
if (error instanceof Error) {
|
|
15955
|
-
console.error(
|
|
16446
|
+
console.error(chalk22.red(`
|
|
15956
16447
|
\u2717 ${error.message}
|
|
15957
16448
|
`));
|
|
15958
16449
|
}
|
|
@@ -15964,7 +16455,7 @@ program.command("init").description("Create a replicas.json or replicas.yaml con
|
|
|
15964
16455
|
initCommand(options);
|
|
15965
16456
|
} catch (error) {
|
|
15966
16457
|
if (error instanceof Error) {
|
|
15967
|
-
console.error(
|
|
16458
|
+
console.error(chalk22.red(`
|
|
15968
16459
|
\u2717 ${error.message}
|
|
15969
16460
|
`));
|
|
15970
16461
|
}
|
|
@@ -15976,7 +16467,7 @@ program.command("logout").description("Clear stored credentials").action(() => {
|
|
|
15976
16467
|
logoutCommand();
|
|
15977
16468
|
} catch (error) {
|
|
15978
16469
|
if (error instanceof Error) {
|
|
15979
|
-
console.error(
|
|
16470
|
+
console.error(chalk22.red(`
|
|
15980
16471
|
\u2717 ${error.message}
|
|
15981
16472
|
`));
|
|
15982
16473
|
}
|
|
@@ -15988,7 +16479,7 @@ program.command("whoami").description("Display current authenticated user").acti
|
|
|
15988
16479
|
await whoamiCommand();
|
|
15989
16480
|
} catch (error) {
|
|
15990
16481
|
if (error instanceof Error) {
|
|
15991
|
-
console.error(
|
|
16482
|
+
console.error(chalk22.red(`
|
|
15992
16483
|
\u2717 ${error.message}
|
|
15993
16484
|
`));
|
|
15994
16485
|
}
|
|
@@ -16000,7 +16491,7 @@ program.command("codex-auth").description("Authenticate Replicas with your Codex
|
|
|
16000
16491
|
await codexAuthCommand(options);
|
|
16001
16492
|
} catch (error) {
|
|
16002
16493
|
if (error instanceof Error) {
|
|
16003
|
-
console.error(
|
|
16494
|
+
console.error(chalk22.red(`
|
|
16004
16495
|
\u2717 ${error.message}
|
|
16005
16496
|
`));
|
|
16006
16497
|
}
|
|
@@ -16012,7 +16503,7 @@ program.command("claude-auth").description("Authenticate Replicas with your Clau
|
|
|
16012
16503
|
await claudeAuthCommand(options);
|
|
16013
16504
|
} catch (error) {
|
|
16014
16505
|
if (error instanceof Error) {
|
|
16015
|
-
console.error(
|
|
16506
|
+
console.error(chalk22.red(`
|
|
16016
16507
|
\u2717 ${error.message}
|
|
16017
16508
|
`));
|
|
16018
16509
|
}
|
|
@@ -16025,7 +16516,7 @@ org.command("switch").description("Switch to a different organization").action(a
|
|
|
16025
16516
|
await orgSwitchCommand();
|
|
16026
16517
|
} catch (error) {
|
|
16027
16518
|
if (error instanceof Error) {
|
|
16028
|
-
console.error(
|
|
16519
|
+
console.error(chalk22.red(`
|
|
16029
16520
|
\u2717 ${error.message}
|
|
16030
16521
|
`));
|
|
16031
16522
|
}
|
|
@@ -16037,7 +16528,7 @@ org.action(async () => {
|
|
|
16037
16528
|
await orgCommand();
|
|
16038
16529
|
} catch (error) {
|
|
16039
16530
|
if (error instanceof Error) {
|
|
16040
|
-
console.error(
|
|
16531
|
+
console.error(chalk22.red(`
|
|
16041
16532
|
\u2717 ${error.message}
|
|
16042
16533
|
`));
|
|
16043
16534
|
}
|
|
@@ -16049,7 +16540,7 @@ program.command("connect <workspace-name>").description("Connect to a workspace
|
|
|
16049
16540
|
await connectCommand(workspaceName);
|
|
16050
16541
|
} catch (error) {
|
|
16051
16542
|
if (error instanceof Error) {
|
|
16052
|
-
console.error(
|
|
16543
|
+
console.error(chalk22.red(`
|
|
16053
16544
|
\u2717 ${error.message}
|
|
16054
16545
|
`));
|
|
16055
16546
|
}
|
|
@@ -16061,7 +16552,7 @@ program.command("code <workspace-name>").description("Open a workspace in VSCode
|
|
|
16061
16552
|
await codeCommand(workspaceName);
|
|
16062
16553
|
} catch (error) {
|
|
16063
16554
|
if (error instanceof Error) {
|
|
16064
|
-
console.error(
|
|
16555
|
+
console.error(chalk22.red(`
|
|
16065
16556
|
\u2717 ${error.message}
|
|
16066
16557
|
`));
|
|
16067
16558
|
}
|
|
@@ -16074,7 +16565,7 @@ config.command("get <key>").description("Get a configuration value").action(asyn
|
|
|
16074
16565
|
await configGetCommand(key);
|
|
16075
16566
|
} catch (error) {
|
|
16076
16567
|
if (error instanceof Error) {
|
|
16077
|
-
console.error(
|
|
16568
|
+
console.error(chalk22.red(`
|
|
16078
16569
|
\u2717 ${error.message}
|
|
16079
16570
|
`));
|
|
16080
16571
|
}
|
|
@@ -16086,7 +16577,7 @@ config.command("set <key> <value>").description("Set a configuration value").act
|
|
|
16086
16577
|
await configSetCommand(key, value);
|
|
16087
16578
|
} catch (error) {
|
|
16088
16579
|
if (error instanceof Error) {
|
|
16089
|
-
console.error(
|
|
16580
|
+
console.error(chalk22.red(`
|
|
16090
16581
|
\u2717 ${error.message}
|
|
16091
16582
|
`));
|
|
16092
16583
|
}
|
|
@@ -16098,7 +16589,7 @@ config.command("list").description("List all configuration values").action(async
|
|
|
16098
16589
|
await configListCommand();
|
|
16099
16590
|
} catch (error) {
|
|
16100
16591
|
if (error instanceof Error) {
|
|
16101
|
-
console.error(
|
|
16592
|
+
console.error(chalk22.red(`
|
|
16102
16593
|
\u2717 ${error.message}
|
|
16103
16594
|
`));
|
|
16104
16595
|
}
|
|
@@ -16110,7 +16601,7 @@ program.command("list").description("List all replicas").option("-p, --page <pag
|
|
|
16110
16601
|
await replicaListCommand(options);
|
|
16111
16602
|
} catch (error) {
|
|
16112
16603
|
if (error instanceof Error) {
|
|
16113
|
-
console.error(
|
|
16604
|
+
console.error(chalk22.red(`
|
|
16114
16605
|
\u2717 ${error.message}
|
|
16115
16606
|
`));
|
|
16116
16607
|
}
|
|
@@ -16122,7 +16613,7 @@ program.command("get <id>").description("Get replica details by ID").action(asyn
|
|
|
16122
16613
|
await replicaGetCommand(id);
|
|
16123
16614
|
} catch (error) {
|
|
16124
16615
|
if (error instanceof Error) {
|
|
16125
|
-
console.error(
|
|
16616
|
+
console.error(chalk22.red(`
|
|
16126
16617
|
\u2717 ${error.message}
|
|
16127
16618
|
`));
|
|
16128
16619
|
}
|
|
@@ -16134,7 +16625,7 @@ program.command("create [name]").description("Create a new replica").option("-m,
|
|
|
16134
16625
|
await replicaCreateCommand(name, options);
|
|
16135
16626
|
} catch (error) {
|
|
16136
16627
|
if (error instanceof Error) {
|
|
16137
|
-
console.error(
|
|
16628
|
+
console.error(chalk22.red(`
|
|
16138
16629
|
\u2717 ${error.message}
|
|
16139
16630
|
`));
|
|
16140
16631
|
}
|
|
@@ -16146,7 +16637,7 @@ program.command("send <id>").description("Send a message to a replica").option("
|
|
|
16146
16637
|
await replicaSendCommand(id, options);
|
|
16147
16638
|
} catch (error) {
|
|
16148
16639
|
if (error instanceof Error) {
|
|
16149
|
-
console.error(
|
|
16640
|
+
console.error(chalk22.red(`
|
|
16150
16641
|
\u2717 ${error.message}
|
|
16151
16642
|
`));
|
|
16152
16643
|
}
|
|
@@ -16158,7 +16649,7 @@ program.command("delete <id>").description("Delete a replica").option("-f, --for
|
|
|
16158
16649
|
await replicaDeleteCommand(id, options);
|
|
16159
16650
|
} catch (error) {
|
|
16160
16651
|
if (error instanceof Error) {
|
|
16161
|
-
console.error(
|
|
16652
|
+
console.error(chalk22.red(`
|
|
16162
16653
|
\u2717 ${error.message}
|
|
16163
16654
|
`));
|
|
16164
16655
|
}
|
|
@@ -16170,7 +16661,7 @@ program.command("read <id>").description("Read conversation history of a replica
|
|
|
16170
16661
|
await replicaReadCommand(id, options);
|
|
16171
16662
|
} catch (error) {
|
|
16172
16663
|
if (error instanceof Error) {
|
|
16173
|
-
console.error(
|
|
16664
|
+
console.error(chalk22.red(`
|
|
16174
16665
|
\u2717 ${error.message}
|
|
16175
16666
|
`));
|
|
16176
16667
|
}
|
|
@@ -16183,7 +16674,7 @@ automation.command("list").description("List all automations").option("-p, --pag
|
|
|
16183
16674
|
await automationListCommand(options);
|
|
16184
16675
|
} catch (error) {
|
|
16185
16676
|
if (error instanceof Error) {
|
|
16186
|
-
console.error(
|
|
16677
|
+
console.error(chalk22.red(`
|
|
16187
16678
|
\u2717 ${error.message}
|
|
16188
16679
|
`));
|
|
16189
16680
|
}
|
|
@@ -16195,7 +16686,7 @@ automation.command("get <id>").description("Get automation details by ID").actio
|
|
|
16195
16686
|
await automationGetCommand(id);
|
|
16196
16687
|
} catch (error) {
|
|
16197
16688
|
if (error instanceof Error) {
|
|
16198
|
-
console.error(
|
|
16689
|
+
console.error(chalk22.red(`
|
|
16199
16690
|
\u2717 ${error.message}
|
|
16200
16691
|
`));
|
|
16201
16692
|
}
|
|
@@ -16210,7 +16701,7 @@ automation.command("create [name]").description("Create a new automation").optio
|
|
|
16210
16701
|
});
|
|
16211
16702
|
} catch (error) {
|
|
16212
16703
|
if (error instanceof Error) {
|
|
16213
|
-
console.error(
|
|
16704
|
+
console.error(chalk22.red(`
|
|
16214
16705
|
\u2717 ${error.message}
|
|
16215
16706
|
`));
|
|
16216
16707
|
}
|
|
@@ -16222,7 +16713,7 @@ automation.command("edit <id>").description("Edit an existing automation").optio
|
|
|
16222
16713
|
await automationEditCommand(id, options);
|
|
16223
16714
|
} catch (error) {
|
|
16224
16715
|
if (error instanceof Error) {
|
|
16225
|
-
console.error(
|
|
16716
|
+
console.error(chalk22.red(`
|
|
16226
16717
|
\u2717 ${error.message}
|
|
16227
16718
|
`));
|
|
16228
16719
|
}
|
|
@@ -16234,7 +16725,7 @@ automation.command("run <id>").description("Manually trigger an automation (cron
|
|
|
16234
16725
|
await automationRunCommand(id);
|
|
16235
16726
|
} catch (error) {
|
|
16236
16727
|
if (error instanceof Error) {
|
|
16237
|
-
console.error(
|
|
16728
|
+
console.error(chalk22.red(`
|
|
16238
16729
|
\u2717 ${error.message}
|
|
16239
16730
|
`));
|
|
16240
16731
|
}
|
|
@@ -16246,7 +16737,7 @@ automation.command("delete <id>").description("Delete an automation").option("-f
|
|
|
16246
16737
|
await automationDeleteCommand(id, options);
|
|
16247
16738
|
} catch (error) {
|
|
16248
16739
|
if (error instanceof Error) {
|
|
16249
|
-
console.error(
|
|
16740
|
+
console.error(chalk22.red(`
|
|
16250
16741
|
\u2717 ${error.message}
|
|
16251
16742
|
`));
|
|
16252
16743
|
}
|
|
@@ -16258,7 +16749,7 @@ automation.action(async () => {
|
|
|
16258
16749
|
await automationListCommand({});
|
|
16259
16750
|
} catch (error) {
|
|
16260
16751
|
if (error instanceof Error) {
|
|
16261
|
-
console.error(
|
|
16752
|
+
console.error(chalk22.red(`
|
|
16262
16753
|
\u2717 ${error.message}
|
|
16263
16754
|
`));
|
|
16264
16755
|
}
|
|
@@ -16271,7 +16762,7 @@ repos.command("list").description("List all repositories").action(async () => {
|
|
|
16271
16762
|
await repositoriesListCommand();
|
|
16272
16763
|
} catch (error) {
|
|
16273
16764
|
if (error instanceof Error) {
|
|
16274
|
-
console.error(
|
|
16765
|
+
console.error(chalk22.red(`
|
|
16275
16766
|
\u2717 ${error.message}
|
|
16276
16767
|
`));
|
|
16277
16768
|
}
|
|
@@ -16283,7 +16774,7 @@ repos.action(async () => {
|
|
|
16283
16774
|
await repositoriesListCommand();
|
|
16284
16775
|
} catch (error) {
|
|
16285
16776
|
if (error instanceof Error) {
|
|
16286
|
-
console.error(
|
|
16777
|
+
console.error(chalk22.red(`
|
|
16287
16778
|
\u2717 ${error.message}
|
|
16288
16779
|
`));
|
|
16289
16780
|
}
|
|
@@ -16296,7 +16787,7 @@ environment.command("list").description("List all environments").action(async ()
|
|
|
16296
16787
|
await environmentListCommand();
|
|
16297
16788
|
} catch (error) {
|
|
16298
16789
|
if (error instanceof Error) {
|
|
16299
|
-
console.error(
|
|
16790
|
+
console.error(chalk22.red(`
|
|
16300
16791
|
\u2717 ${error.message}
|
|
16301
16792
|
`));
|
|
16302
16793
|
}
|
|
@@ -16308,7 +16799,7 @@ environment.command("get <id-or-name>").description('Get an environment by ID or
|
|
|
16308
16799
|
await environmentGetCommand(idOrName);
|
|
16309
16800
|
} catch (error) {
|
|
16310
16801
|
if (error instanceof Error) {
|
|
16311
|
-
console.error(
|
|
16802
|
+
console.error(chalk22.red(`
|
|
16312
16803
|
\u2717 ${error.message}
|
|
16313
16804
|
`));
|
|
16314
16805
|
}
|
|
@@ -16320,7 +16811,7 @@ environment.command("create [name]").description("Create a new environment").opt
|
|
|
16320
16811
|
await environmentCreateCommand(name, options);
|
|
16321
16812
|
} catch (error) {
|
|
16322
16813
|
if (error instanceof Error) {
|
|
16323
|
-
console.error(
|
|
16814
|
+
console.error(chalk22.red(`
|
|
16324
16815
|
\u2717 ${error.message}
|
|
16325
16816
|
`));
|
|
16326
16817
|
}
|
|
@@ -16332,7 +16823,7 @@ environment.command("edit <id-or-name>").description("Edit an environment").opti
|
|
|
16332
16823
|
await environmentEditCommand(idOrName, options);
|
|
16333
16824
|
} catch (error) {
|
|
16334
16825
|
if (error instanceof Error) {
|
|
16335
|
-
console.error(
|
|
16826
|
+
console.error(chalk22.red(`
|
|
16336
16827
|
\u2717 ${error.message}
|
|
16337
16828
|
`));
|
|
16338
16829
|
}
|
|
@@ -16344,7 +16835,7 @@ environment.command("delete <id-or-name>").description("Delete an environment").
|
|
|
16344
16835
|
await environmentDeleteCommand(idOrName, options);
|
|
16345
16836
|
} catch (error) {
|
|
16346
16837
|
if (error instanceof Error) {
|
|
16347
|
-
console.error(
|
|
16838
|
+
console.error(chalk22.red(`
|
|
16348
16839
|
\u2717 ${error.message}
|
|
16349
16840
|
`));
|
|
16350
16841
|
}
|
|
@@ -16357,7 +16848,7 @@ envVars.command("list <env>").description("List variables in an environment (val
|
|
|
16357
16848
|
await envVarsListCommand(env, options);
|
|
16358
16849
|
} catch (error) {
|
|
16359
16850
|
if (error instanceof Error) {
|
|
16360
|
-
console.error(
|
|
16851
|
+
console.error(chalk22.red(`
|
|
16361
16852
|
\u2717 ${error.message}
|
|
16362
16853
|
`));
|
|
16363
16854
|
}
|
|
@@ -16369,7 +16860,7 @@ envVars.command("set <env> <key> <value>").description("Create or update a varia
|
|
|
16369
16860
|
await envVarsSetCommand(env, key, value);
|
|
16370
16861
|
} catch (error) {
|
|
16371
16862
|
if (error instanceof Error) {
|
|
16372
|
-
console.error(
|
|
16863
|
+
console.error(chalk22.red(`
|
|
16373
16864
|
\u2717 ${error.message}
|
|
16374
16865
|
`));
|
|
16375
16866
|
}
|
|
@@ -16381,7 +16872,7 @@ envVars.command("delete <env> <key-or-id>").description("Delete a variable by ke
|
|
|
16381
16872
|
await envVarsDeleteCommand(env, keyOrId, options);
|
|
16382
16873
|
} catch (error) {
|
|
16383
16874
|
if (error instanceof Error) {
|
|
16384
|
-
console.error(
|
|
16875
|
+
console.error(chalk22.red(`
|
|
16385
16876
|
\u2717 ${error.message}
|
|
16386
16877
|
`));
|
|
16387
16878
|
}
|
|
@@ -16394,7 +16885,7 @@ envFiles.command("list <env>").description("List files in an environment").actio
|
|
|
16394
16885
|
await envFilesListCommand(env);
|
|
16395
16886
|
} catch (error) {
|
|
16396
16887
|
if (error instanceof Error) {
|
|
16397
|
-
console.error(
|
|
16888
|
+
console.error(chalk22.red(`
|
|
16398
16889
|
\u2717 ${error.message}
|
|
16399
16890
|
`));
|
|
16400
16891
|
}
|
|
@@ -16406,7 +16897,7 @@ envFiles.command("set <env> <destination-path>").description("Create or update a
|
|
|
16406
16897
|
await envFilesSetCommand(env, destinationPath, options);
|
|
16407
16898
|
} catch (error) {
|
|
16408
16899
|
if (error instanceof Error) {
|
|
16409
|
-
console.error(
|
|
16900
|
+
console.error(chalk22.red(`
|
|
16410
16901
|
\u2717 ${error.message}
|
|
16411
16902
|
`));
|
|
16412
16903
|
}
|
|
@@ -16418,7 +16909,7 @@ envFiles.command("delete <env> <path-or-id>").description("Delete a file by dest
|
|
|
16418
16909
|
await envFilesDeleteCommand(env, pathOrId, options);
|
|
16419
16910
|
} catch (error) {
|
|
16420
16911
|
if (error instanceof Error) {
|
|
16421
|
-
console.error(
|
|
16912
|
+
console.error(chalk22.red(`
|
|
16422
16913
|
\u2717 ${error.message}
|
|
16423
16914
|
`));
|
|
16424
16915
|
}
|
|
@@ -16430,7 +16921,7 @@ environment.action(async () => {
|
|
|
16430
16921
|
await environmentListCommand();
|
|
16431
16922
|
} catch (error) {
|
|
16432
16923
|
if (error instanceof Error) {
|
|
16433
|
-
console.error(
|
|
16924
|
+
console.error(chalk22.red(`
|
|
16434
16925
|
\u2717 ${error.message}
|
|
16435
16926
|
`));
|
|
16436
16927
|
}
|
|
@@ -16442,7 +16933,7 @@ program.command("interact").alias("i").description("Launch the interactive termi
|
|
|
16442
16933
|
await interactiveCommand();
|
|
16443
16934
|
} catch (error) {
|
|
16444
16935
|
if (error instanceof Error) {
|
|
16445
|
-
console.error(
|
|
16936
|
+
console.error(chalk22.red(`
|
|
16446
16937
|
\u2717 ${error.message}
|
|
16447
16938
|
`));
|
|
16448
16939
|
}
|
|
@@ -16493,7 +16984,7 @@ if (isAgentMode()) {
|
|
|
16493
16984
|
await previewAddCommand(workspaceId, options);
|
|
16494
16985
|
} catch (error) {
|
|
16495
16986
|
if (error instanceof Error) {
|
|
16496
|
-
console.error(
|
|
16987
|
+
console.error(chalk22.red(`
|
|
16497
16988
|
\u2717 ${error.message}
|
|
16498
16989
|
`));
|
|
16499
16990
|
}
|
|
@@ -16505,7 +16996,7 @@ if (isAgentMode()) {
|
|
|
16505
16996
|
await previewListCommand(workspaceId);
|
|
16506
16997
|
} catch (error) {
|
|
16507
16998
|
if (error instanceof Error) {
|
|
16508
|
-
console.error(
|
|
16999
|
+
console.error(chalk22.red(`
|
|
16509
17000
|
\u2717 ${error.message}
|
|
16510
17001
|
`));
|
|
16511
17002
|
}
|
|
@@ -16517,7 +17008,7 @@ if (isAgentMode()) {
|
|
|
16517
17008
|
await previewRemoveCommand(workspaceId, options);
|
|
16518
17009
|
} catch (error) {
|
|
16519
17010
|
if (error instanceof Error) {
|
|
16520
|
-
console.error(
|
|
17011
|
+
console.error(chalk22.red(`
|
|
16521
17012
|
\u2717 ${error.message}
|
|
16522
17013
|
`));
|
|
16523
17014
|
}
|
|
@@ -16532,7 +17023,7 @@ if (isAgentMode()) {
|
|
|
16532
17023
|
await mediaUploadCommand(files, options);
|
|
16533
17024
|
} catch (error) {
|
|
16534
17025
|
if (error instanceof Error) {
|
|
16535
|
-
console.error(
|
|
17026
|
+
console.error(chalk22.red(`
|
|
16536
17027
|
\u2717 ${error.message}
|
|
16537
17028
|
`));
|
|
16538
17029
|
}
|
|
@@ -16544,19 +17035,47 @@ if (isAgentMode()) {
|
|
|
16544
17035
|
await mediaListCommand(options);
|
|
16545
17036
|
} catch (error) {
|
|
16546
17037
|
if (error instanceof Error) {
|
|
16547
|
-
console.error(
|
|
17038
|
+
console.error(chalk22.red(`
|
|
16548
17039
|
\u2717 ${error.message}
|
|
16549
17040
|
`));
|
|
16550
17041
|
}
|
|
16551
17042
|
process.exit(1);
|
|
16552
17043
|
}
|
|
16553
17044
|
});
|
|
17045
|
+
const computer = program.command("computer").description("Drive the workspace Linux desktop (mouse, keyboard, screenshots, screen recording, live preview)");
|
|
17046
|
+
const wrap = (fn) => async (...args) => {
|
|
17047
|
+
try {
|
|
17048
|
+
await fn(...args);
|
|
17049
|
+
} catch (error) {
|
|
17050
|
+
if (error instanceof Error) {
|
|
17051
|
+
console.error(chalk22.red(`
|
|
17052
|
+
\u2717 ${error.message}
|
|
17053
|
+
`));
|
|
17054
|
+
}
|
|
17055
|
+
process.exit(1);
|
|
17056
|
+
}
|
|
17057
|
+
};
|
|
17058
|
+
computer.command("start").description("Ensure the desktop stack is running and create an authenticated noVNC preview URL. Prints the viewer URL.").option("--port <port>", "noVNC port to expose (default: 6080)").option("--display <id>", "X display to use (default: :99)").option("--size <WxH>", "Display size, e.g. 1920x1080 (default: 1920x1080)").action(wrap((options) => computerStartCommand(options)));
|
|
17059
|
+
computer.command("stop").description("Tear down the noVNC preview. Desktop services stay running for the next start.").option("--port <port>", "noVNC port to unregister (default: 6080)").action(wrap((options) => computerStopCommand(options)));
|
|
17060
|
+
computer.command("status").description("Show which desktop services are running and the active preview URL").action(wrap(() => computerStatusCommand()));
|
|
17061
|
+
computer.command("screenshot <path>").description("Capture the current desktop to a PNG file. Use the path with `replicas media upload` to share it.").action(wrap((path6) => computerScreenshotCommand(path6)));
|
|
17062
|
+
computer.command("click <x> <y>").description("Move to (x, y) and click. Coordinates are in pixels on the workspace display.").option("-b, --button <n>", "Mouse button (1=left, 2=middle, 3=right). Default 1.").option("--double", "Double-click instead of single-click").option("--modifiers <mods>", "Hold modifier keys during the click, e.g. ctrl or ctrl+shift").action(wrap((x, y, options) => computerClickCommand(x, y, options)));
|
|
17063
|
+
computer.command("move <x> <y>").description("Move the mouse to (x, y) without clicking").action(wrap((x, y) => computerMoveCommand(x, y)));
|
|
17064
|
+
computer.command("type <text>").description("Type a literal string into the focused field. Use `key` for key combos like ctrl+l.").option("--delay <ms>", "Per-character delay in ms (default 12 \u2248 80 wpm)").action(wrap((text, options) => computerTypeCommand(text, options)));
|
|
17065
|
+
computer.command("key <combo>").description("Press a key combo, e.g. Return, Escape, ctrl+l, ctrl+shift+t. Same syntax as xdotool key.").action(wrap((combo) => computerKeyCommand(combo)));
|
|
17066
|
+
computer.command("scroll <direction>").description("Scroll up | down | left | right. Optionally provide --x / --y to hover before scrolling.").option("--amount <n>", "Wheel ticks (default 3)").option("--x <x>", "Hover x before scrolling").option("--y <y>", "Hover y before scrolling").action(wrap((direction, options) => computerScrollCommand(direction, options)));
|
|
17067
|
+
computer.command("drag <fromX> <fromY> <toX> <toY>").description("Press the left mouse button at (fromX, fromY), drag to (toX, toY), release.").action(wrap((fx, fy, tx, ty) => computerDragCommand(fx, fy, tx, ty)));
|
|
17068
|
+
computer.command("launch <app> [args...]").description("Launch an app on the workspace display. Aliases: chrome, chromium, firefox, terminal.").action(wrap((app, args) => computerLaunchCommand(app, args)));
|
|
17069
|
+
const record = computer.command("record").description("Screen-record the workspace display to an MP4 (1080p 60fps libx264).");
|
|
17070
|
+
record.command("start <path>").description("Start recording to <path>. Output is fragmented MP4 (safe if the workspace dies mid-record).").option("--fps <n>", "Frame rate (default 60)").action(wrap((path6, options) => computerRecordStartCommand(path6, options)));
|
|
17071
|
+
record.command("stop").description("Stop the active recording and finalize the MP4. Prints the output path.").action(wrap(() => computerRecordStopCommand()));
|
|
16554
17072
|
const allowed = /* @__PURE__ */ new Set([
|
|
16555
17073
|
"init",
|
|
16556
17074
|
"whoami",
|
|
16557
17075
|
"connect",
|
|
16558
17076
|
"preview",
|
|
16559
17077
|
"media",
|
|
17078
|
+
"computer",
|
|
16560
17079
|
"automation",
|
|
16561
17080
|
"repos",
|
|
16562
17081
|
"environment"
|